Search code examples
jsontraefik

How to pretty format the Traefik log in JSON?


I have a Traefik service with the following configuration:

version: "3.9"
services:
  reverse-proxy:
    image: traefik:v2.3.4
    networks:
      common:
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    command:
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=common"
      - "--entrypoints.web.address=:80"
#      - "--entrypoints.websecure.address=:443"
      - "--global.sendAnonymousUsage=true"
      # Set a debug level custom log file
      - "--log.level=DEBUG"
      - "--log.format=json"
      - "--log.filePath=/var/log/traefik.log"
      - "--accessLog.filePath=/var/log/access.log"
      # Enable the Traefik dashboard
      - "--api.dashboard=true"
#      - "traefik.constraint-label=common" TODO
    deploy:
      placement:
        constraints:
          - node.role == manager
      labels:
        # Expose the Traefik dashboard
        - "traefik.enable=true"
        - "traefik.http.routers.dashboard.service=api@internal"
        - "traefik.http.services.traefik.loadbalancer.server.port=888" # A port number required by Docker Swarm but not being used in fact
        - "traefik.http.routers.dashboard.rule=Host(`traefik.learnintouch.com`)"
        - "traefik.http.routers.traefik.entrypoints=web"
#        - "traefik.http.routers.traefik.entrypoints=websecure"
        # Basic HTTP authentication to secure the dashboard access
        - "traefik.http.routers.traefik.middlewares=traefik-auth"
        - "traefik.http.middlewares.traefik-auth.basicauth.users=stephane:$$apr1$$m72sBfSg$$7.NRvy75AZXAMtH3C2YTz/"
    volumes:
      # So that Traefik can listen to the Docker events
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "~/dev/docker/projects/common/volumes/logs/traefik.service.log:/var/log/traefik.log"
      - "~/dev/docker/projects/common/volumes/logs/traefik.access.log:/var/log/access.log"

Then I watch the log with the command:

stephane@stephane-pc:~$ tail -f dev/docker/projects/common/volumes/logs/traefik.service.log 
{"level":"info","msg":"I have to go...","time":"2021-07-03T10:18:10Z"}
{"level":"info","msg":"Stopping server gracefully","time":"2021-07-03T10:18:10Z"}
{"entryPointName":"web","level":"debug","msg":"Waiting 10s seconds before killing connections.","time":"2021-07-03T10:18:10Z"}
{"entryPointName":"web","level":"error","msg":"accept tcp [::]:80: use of closed network connection","time":"2021-07-03T10:18:10Z"}

I expected the log to be formatted in JSON with indentation.

So I copy-pasted the non indented JSON output in an online JSON formatter but it only indented part of it, making the whole thing useless.


Solution

  • Your problem is that Traefik does not output a single JSON document, but one JSON document per line. You could beautify all documents using xargs and jq:

    tail -f dev/docker/projects/common/volumes/logs/traefik.service.log | xargs -n 1 -d "\n" -- bash -c 'echo "$1" | jq' _
    

    In your example, this will result in this output (even with syntax highlighting if your terminal supports that):

    {
      "level": "info",
      "msg": "I have to go...",
      "time": "2021-07-03T10:18:10Z"
    }
    {
      "level": "info",
      "msg": "Stopping server gracefully",
      "time": "2021-07-03T10:18:10Z"
    }
    {
      "entryPointName": "web",
      "level": "debug",
      "msg": "Waiting 10s seconds before killing connections.",
      "time": "2021-07-03T10:18:10Z"
    }
    {
      "entryPointName": "web",
      "level": "error",
      "msg": "accept tcp [::]:80: use of closed network connection",
      "time": "2021-07-03T10:18:10Z"
    }