Check logs#
Here we will take a closer look at working with logs in Docker. You need to use the docker logs <container name>
command to print the standard output of the container.
Check docker container logs page of the official documentation.
Record time (-t
)#
In addition to the messages in the log, you can print out the time when the recording was made by using -t
option.
The following example adds the execution time to each message in the log.
docker run \
--name test_container -d --rm alpine \
sh -c "while true; do echo message; sleep 1; done" &> /dev/null
sleep 5
docker logs -t test_container
docker stop test_container &> /dev/null
2024-07-17T14:29:42.554728481Z message
2024-07-17T14:29:43.555603029Z message
2024-07-17T14:29:44.556912233Z message
2024-07-17T14:29:45.558109366Z message
2024-07-17T14:29:46.559459793Z message
2024-07-17T14:29:47.560961483Z message
Follow log ouput#
You can stack the current terminal and print log messages as they’re generated by container. Use the -f
option to do this.
The following example starts a docker container that slowly prints 10 messages in detach mode. The fact that the execution flow has been returned to the program is indicated by the message "Returned to the program"
. And only after that we start docker logs -f test_container
, which will gradually print all the required messages to the output.
docker run \
--name test_container --rm -d alpine \
sh -c 'for num in $(seq 1 10); do date "+%T"; sleep 1; done'
echo "Returned to the programm"
docker logs -f test_container
1ba92ea19e1c6e6c22cb600858ecab9b368a7a88c7c354b1b2c6030c6eb86544
Returned to the programm
12:49:42
12:49:43
12:49:44
12:49:45
12:49:46
12:49:47
12:49:48
12:49:50
12:49:51
12:49:52
Time range#
You can specify a date range for the container to use when searching records by utilizing the --since
and --until
parameters. These parameters accept RFC 3339 dates (including the Nano variation), UNIX timestamps, or Go duration strings.
As an example, consider conatiner, which generates a set of dates in RFC 3339 format in it’s default output.
docker run \
--name time_range_example --rm -itd alpine \
sh -c '
for num in $(seq 1 10);
do date -u +"%Y-%m-%dT%H:%M:%SZ";
sleep 1;
done;
sh'
sleep 10
23eb1c8dc8043b8a6e1a67719be82130a13653b486bd4f58c3fa8a6466f550e7
The following cell shows full logs available in the container.
docker logs time_range_example
2024-12-03T12:51:24Z
2024-12-03T12:51:25Z
2024-12-03T12:51:26Z
2024-12-03T12:51:27Z
2024-12-03T12:51:28Z
2024-12-03T12:51:29Z
2024-12-03T12:51:30Z
2024-12-03T12:51:31Z
2024-12-03T12:51:32Z
2024-12-03T12:51:33Z
The following cell borders query the logs by date.
docker logs \
--since 2024-12-03T12:51:27Z\
--until 2024-12-03T12:51:30Z\
time_range_example
2024-12-03T12:51:27Z
2024-12-03T12:51:28Z
2024-12-03T12:51:29Z
Don’t forget to stop the container.
docker stop time_range_example
time_range_example
Logs on the host#
Host stores logs of all containers in a special JSON format file. You can access this file if you need to. Simply use the command docker inspect --format "{{.LogPath}}" <container name>
to get the path to the logs on the host.
Example for this section is really complicated. This is due to the following reasons:
Docker usually stores logs in folders with specific access rights. To solve this, we use docker in docker, we would be able to check the logs of the container that is running inside another container.
Docker in Docker creates it’s own volumes. To avoid flooding the system, we’ll use docker compose, which will help us to manage the removal of the volume with the corresponding container.
So here is the dockercompose file that defines the docker container and its volume:
cat << EOF > compose.yml
services:
dind:
image: docker
privileged: true
container_name: my_docker
volumes:
- dind_data:/var/lib/docker
volumes:
dind_data:
driver: local
EOF
Now we run dockercompose and run a nested docker container that prints messages to the standard output.
docker compose up -d &> /dev/null
sleep 5
docker exec my_docker \
docker run -d --name test_alpine alpine \
sh -c "while true; do $(echo date "+%T"); sleep 1; done"&> /dev/null
Let’s check if all is well by calling docker logs
for a nested container.
docker exec my_docker \
docker logs test_alpine | tail -n 5
08:41:10
08:41:11
08:41:12
08:41:13
08:41:14
By applying docker inspect --format "{{.LogPath}}" test_alpine
we get the path to the logs in the container with docker to the logs_path
variable. Then print value of this variable.
export logs_path=$(docker exec my_docker \
docker inspect --format "{{.LogPath}}" test_alpine)
echo $logs_path
/var/lib/docker/containers/86a63d38dd43a4e9ad794a2e79b1f79e8ac3971af82398caaa59503d463a5ea4/86a63d38dd43a4e9ad794a2e79b1f79e8ac3971af82398caaa59503d463a5ea4-json.log
Let’s check file with logs from the container with docker.
docker exec my_docker cat $logs_path | tail -n 10
{"log":"08:41:09\n","stream":"stdout","time":"2024-07-18T08:41:09.359119209Z"}
{"log":"08:41:10\n","stream":"stdout","time":"2024-07-18T08:41:10.361869059Z"}
{"log":"08:41:11\n","stream":"stdout","time":"2024-07-18T08:41:11.36443104Z"}
{"log":"08:41:12\n","stream":"stdout","time":"2024-07-18T08:41:12.366957688Z"}
{"log":"08:41:13\n","stream":"stdout","time":"2024-07-18T08:41:13.369547386Z"}
{"log":"08:41:14\n","stream":"stdout","time":"2024-07-18T08:41:14.372117445Z"}
{"log":"08:41:15\n","stream":"stdout","time":"2024-07-18T08:41:15.373621555Z"}
{"log":"08:41:16\n","stream":"stdout","time":"2024-07-18T08:41:16.375638904Z"}
{"log":"08:41:17\n","stream":"stdout","time":"2024-07-18T08:41:17.378278589Z"}
{"log":"08:41:18\n","stream":"stdout","time":"2024-07-18T08:41:18.380441036Z"}
So here is a json file with one line for each message. Each message has keys: log, stream and time.
After all clean the environment.
docker compose stop &> /dev/null
docker compose rm -f &> /dev/null
docker compose down --volumes &> /dev/null
rm compose.yml