Confused about access logs

1. The problem I’m having:

I am currently trying to configure Caddy from FrankenPHP running Laravel Octane. I am confused about the access logs because even if it is “wrapped” under json, I’m only seeing the following logs in my docker container’s stderr

Ultimately, I’m moving from Nginx to Caddy and I want to format the logs as JSON and add an additional field as well called “origin” with the value “caddy”.

2. Error messages and/or full log output:

Logs shown above

3. Caddy version:

FrankenPHP v1.2.5 PHP 8.3.12 Caddy v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

I have Caddy installed via FrankenPHP docker image.

a. System environment:

The docker image I’m using is based on Alpine.

b. Command:

php /app/artisan octane:frankenphp --watch --caddyfile=/app/docker/web/caddy/Caddyfile

c. Service/unit/compose file:

d. My complete Caddy config:

{
	{$CADDY_GLOBAL_OPTIONS}

	admin {$CADDY_SERVER_ADMIN_HOST}:{$CADDY_SERVER_ADMIN_PORT}

	frankenphp {
		worker "{$APP_PUBLIC_PATH}/frankenphp-worker.php" {$CADDY_SERVER_WORKER_COUNT}
	}
}

{$CADDY_SERVER_SERVER_NAME} {
	log {
		level {$CADDY_SERVER_LOG_LEVEL}

		# Redact the authorization query parameter that can be set by Mercure...
		format filter {
			wrap json
			fields {
				uri query {
					replace authorization REDACTED
				}
			}
		}

		output stderr
	}

	route {
		root * "{$APP_PUBLIC_PATH}"
		encode zstd br gzip

		# Security headers
       header {
           X-Frame-Options "SAMEORIGIN"
           X-XSS-Protection "1; mode=block"
           X-Content-Type-Options "nosniff"
           Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
           Referrer-Policy "strict-origin-when-cross-origin"
           Permissions-Policy "geolocation=()"
       }

		# Mercure configuration is injected here...
		{$CADDY_SERVER_EXTRA_DIRECTIVES}

		@static {
            file
            path *.js *.css *.jpg *.jpeg *.gif *.png *.ico *.cur *.gz *.svg *.svgz *.mp4 *.mp3 *.ogg *.ogv *.webm *.htc *.woff2 *.woff
        }

        @staticshort {
            file
            path *.json *.xml *.rss
        }

        # 1 year, similar to h5bp nginx config
        header @static Cache-Control "public, immutable, stale-while-revalidate, max-age=31536000"

        # 1 hour max, gets validated with the origin server
        # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#no-cache
        header @staticshort Cache-Control "no-cache, max-age=3600"

        # Restrict access to dot files and certain file extensions
        @rejected `path('*.bak', '*.conf', '*.dist', '*.fla', '*.ini', '*.inc', '*.inci', '*.log', '*.orig', '*.psd', '*.sh', '*.sql', '*.swo', '*.swp', '*.swop', '*/.*') && !path('*/.well-known/')`
        error @rejected 401

		php_server {
			index frankenphp-worker.php
			# Required for the public/storage/ directory...
			resolve_root_symlink
		}
	}
}

5. Links to relevant resources:

That’s not what Caddy’s log format looks like. Is that being emitted by your PHP app? Your config looks fine, so I’m not sure what’s going on.

FYI you can simplify this to:

		format filter {
			uri query {
				replace authorization REDACTED
			}
		}

Actually, what do you have this set to? If it’s set too high then you wouldn’t see any access logs. (They’re only INFO, and ERROR for 5xx)

1 Like

So I’ve been trying to get the logs to output JSON for the past couple of hours.

It turns out, if my output is “stderr”, the logs come out as shown in the image above even when i specify “format json”.

When I change the output to output file /sample.log, I can see it properly outputs the JSON log here. It could be that if I specify “stderr” as the output, it defaults to something else (guessing “console” from the documentation) and ignores my current format setting.

I’ve resorted to rebuilding xcaddy with transform-encoder and is currently a work in progress… but I really just want to output JSON to my stderr… :frowning:

log {
	level INFO

	format json

	output stderr
}

Do you have anything else running in that container or something? That looks like the octane output, not the Caddy output. It’s probably eating the Caddy output if you write it to stderr. I don’t really know what they do with Caddy’s output. I would hope it’s retained because Caddy’s runtime logs are important to see deprecation warnings and config errors etc. You’ll need to ask for help with them for that.

1 Like

Hi Francis, yes, it looks like you’re right. It is the output of php artisan octane:frankenphp. I’ve given up trying to output JSON here and probably just run an Nginx proxy to caddy for now… which was mentioned here https://laravel.com/docs/11.x/octane#serving-your-application-via-nginx
but seems so redundant since there is already Caddy built into FrankenPHP.

Thanks for the help!

You can have Caddy output its logs to file tho. The log global option configures runtime logs, whereas the log directive inside your site block configures access logs. That should be sufficient for you.

1 Like

I need it to output to stderr though because we’re using ECS Fargate containers. It automatically puts all stderr logs into cloudwatch and that is acutally the goal for our logs.

You can the “raw” JSON logs by passing explicitly the --log-level=info flag. For instance:

php artisan octane:start --log-level=info

dunglas, man - thanks for this! it works!

So I guess octane:start and octane:frankenphp are very similar and octane:start can also be used with Frankenphp.

Both octane:start and octane:frankenphp when passed with --log-level=info outputs the raw JSON.

We’ve been doing load testing on our new Frankenphp images and getting pretty good results! Thanks dunglas!!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.