mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-28 10:02:15 +00:00
# Which Problems Are Solved Zitadel Errors (`zerrors` package) are logged by default. This change add more verbose error reporting through log output, including compatibility with GCP error reporting if enabled. # How the Problems Are Solved Errors can now carry their "report location" and a stack trace. "Report location" is enabled by default, while stack-traces are disabled. Our new [`zitadel/sloggcp`](https://github.com/zitadel/sloggcp) package provides the GCP error reporting handler for `slog`. The following section displays the different formats for the same error (User not found). Newlines and formatting of output is applied for readability and is not part of the actual log output. <details> <summary>Default text output</summary> ``` time=2026-01-15T14:27:20.625+01:00 level=WARN source=/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go:57 msg="User could not be found (COMMAND-bd4ir1mblj)" domain.instance_host=localhost:8080 domain.protocol=http protocol=connect service=zitadel.user.v2.UserService http_method=POST path=/zitadel.user.v2.UserService/DeleteUser request_id=d5keme0ednc50mm74bfg duration=66.761569ms TraceID=319374baa56acc2dbd1d0179f1bbec9f SpanID=541621b0abea7ff1 err.kind=NotFound err.message="User could not be found" err.id=COMMAND-bd4ir1mblj err.reportLocation.filePath=/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go err.reportLocation.lineNumber=140 err.reportLocation.functionName=github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2 ``` </details> <details> <summary>Default JSON output</summary> ```json { "time": "2026-01-15T14:30:59.280497754+01:00", "level": "WARN", "source": { "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError", "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go", "line": 57 }, "msg": "User could not be found (COMMAND-bd4ir1mblj)", "domain": { "instance_host": "localhost:8080", "protocol": "http" }, "protocol": "connect", "service": "zitadel.user.v2.UserService", "http_method": "POST", "path": "/zitadel.user.v2.UserService/DeleteUser", "request_id": "d5keo4oednc6klmo3vbg", "duration": 54229055, "TraceID": "daa21183bd8da5da56969b0ea45f4388", "SpanID": "70da728e3086c104", "err": { "kind": "NotFound", "message": "User could not be found", "id": "COMMAND-bd4ir1mblj", "reportLocation": { "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go", "lineNumber": 140, "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2" } } } ``` </details> When stack trace is enabled, using a standard logger, it is logged under the `stackTrace` key. <details> <summary>Standard JSON format with stack trace</summary> ```json { "time": "2026-01-15T14:36:56.522692368+01:00", "level": "WARN", "source": { "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError", "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go", "line": 57 }, "msg": "User could not be found (COMMAND-bd4ir1mblj)", "domain": { "instance_host": "localhost:8080", "protocol": "http" }, "protocol": "connect", "service": "zitadel.user.v2.UserService", "http_method": "POST", "path": "/zitadel.user.v2.UserService/DeleteUser", "request_id": "d5kequ0ednc7lphvgfn0", "duration": 57820793, "TraceID": "1b05d2b01306f2a64feb1a24b65a89c5", "SpanID": "c5ff35a27a917764", "err": { "kind": "NotFound", "message": "User could not be found", "id": "COMMAND-bd4ir1mblj", "reportLocation": { "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go", "lineNumber": 140, "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2" }, "stackTrace": "goroutine 673 [running]:\nruntime/debug.Stack()\n\t/usr/lib/go/src/runtime/debug/stack.go:26 +0x8e\ngithub.com/zitadel/zitadel/internal/zerrors.newZitadelError(0x5, {0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/zerror.go:139 +0x1df\ngithub.com/zitadel/zitadel/internal/zerrors.ThrowNotFound({0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/not_found.go:6 +0x70\ngithub.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2(0xc0035a4008, {0xa3c1410, 0xc003ded5c0}, {0xc0029693a0, 0x3}, {0x0, 0x0}, {0xd550a60, 0x0, 0x0}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go:140 +0x711\ngithub.com/zitadel/zitadel/internal/api/grpc/user/v2.(*Server).DeleteUser(0xc00272d080, {0xa3c1410, 0xc003ded5c0}, 0xc002ebf180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/user/v2/user.go:191 +0x3f1\nconnectrpc.com/connect.NewUnaryHandler[...].func1({0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:51 +0x17e\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded5c0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ActivityInterceptor.func1.1({0xa3c1410, 0xc003ded5c0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/activity_interceptor.go:20 +0x1b1\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ServiceHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/service_interceptor.go:22 +0x283\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.validate({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180}, 0xc0031bff60)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:35 +0x1bd\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ValidationHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ExecutionHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/execution_interceptor.go:39 +0x376\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.QuotaExhaustedInterceptor.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/quota_interceptor.go:25 +0x42d\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.TranslationHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/translation_interceptor.go:18 +0x94\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.authorize({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180}, 0xc00321c040, {0xa3f0a70, 0xc0004ebc70}, {{0xc000d2fc80, 0x9, 0x9}}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:42 +0x894\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AuthorizationInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:17 +0x151\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.LimitsInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/limits_interceptor.go:31 +0x3b7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.toConnectError({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180}, 0xc00321c060)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:21 +0x8a\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ErrorHandler.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AccessStorageInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/access_interceptor.go:21 +0x814\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.addInstanceByRequestedHost({0xa3c1410, 0xc003db1110}, {0xa405e70, 0xc002ebf180}, 0xc00321c090, {0xa3a63e8, 0xc0002f4a00}, 0xc002dee9b0, {0xc0012106a0, 0x9})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:107 +0xbc3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.setInstance({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180}, 0xc00321c090, {0xa3a63e8, 0xc0002f4a00}, {0xc0012106a0, 0x9}, 0xc002dee9b0, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:61 +0xb05\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.InstanceInterceptor.func1.1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:23 +0x147\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.NoCacheInterceptor.func1.1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/cache_interceptor.go:21 +0x351\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/backend/v3/instrumentation/logging.NewConnectInterceptor.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/backend/v3/instrumentation/logging/connect_handler.go:26 +0x27b\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.RegisterMetrics({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180}, 0xc00321c0d0, {0xcf754f8, 0x3, 0x3}, {0xc0008b8840, 0x4, ...})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:47 +0x3f3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.MetricsHandler.func1.1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:31 +0x111\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\nconnectrpc.com/otelconnect.(*Interceptor).WrapUnary.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected]/interceptor.go:145 +0x16d7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db0ff0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.CallDurationHandler.func1.1({0xa3c1410, 0xc003db0ff0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/call_interceptor.go:15 +0xab\nconnectrpc.com/connect.NewUnaryHandler[...].func2({0x7f11083fe700, 0xc000503a40})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:78 +0x4e2\nconnectrpc.com/connect.(*Handler).ServeHTTP(0xc0004dd420, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:333 +0x870\ngithub.com/zitadel/zitadel/pkg/grpc/user/v2/userconnect.NewUserServiceHandler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/Repositories/zitadel/zitadel/pkg/grpc/user/v2/userconnect/user_service.connect.go:1951 +0x8e5\nnet/http.HandlerFunc.ServeHTTP(0xc002d7a000, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/rs/cors.(*Cors).Handler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/go/pkg/mod/github.com/rs/[email protected].1/cors.go:289 +0x2fd\nnet/http.HandlerFunc.ServeHTTP(0xc000191aa0, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.RobotsTagHandler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/robots_tag_interceptor.go:12 +0x88\nnet/http.HandlerFunc.ServeHTTP(0xc002e14798, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.WithOrigin.func1.1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/origin_interceptor.go:27 +0x42a\nnet/http.HandlerFunc.ServeHTTP(0xc003d89a40, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/gorilla/mux.(*Router).ServeHTTP(0xc002dfa840, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/home/tim/go/pkg/mod/github.com/gorilla/[email protected]/mux.go:212 +0x2bb\ngolang.org/x/net/http2.(*serverConn).runHandler(0xc003e143c0, 0xc0006b22a0, 0xc0002f4000, 0xc002d9ae88)\n\t/home/tim/go/pkg/mod/golang.org/x/[email protected].0/http2/server.go:2424 +0x1b9\ncreated by golang.org/x/net/http2.(*serverConn).scheduleHandler in goroutine 607\n\t/home/tim/go/pkg/mod/golang.org/x/[email protected].0/http2/server.go:2359 +0x1df\n" } } ``` </details> When the GCP error reporting handler is enabled with stack trace, as per [formatting requirements](https://docs.cloud.google.com/error-reporting/docs/formatting-error-messages): - A `@type` field is provided - Some fields are renamed according to Google Cloud Logging specs. (severity, message etc) - The stack strace is appended to the `message` field. - The report location is moved into the top-level `reportLocation` field. The original error is still logged in the `error` field. This is not part of the error reporting API but may still be useful for logging. <details> <summary>GCP error report with stack trace</summary> ```json { "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent", "SpanID": "4fdeae9817ade4f5", "TraceID": "78e31366d4c36e0718a2dcb6ca73d2f3", "domain": { "instance_host": "localhost:8080", "protocol": "http" }, "duration": "82.464896ms", "error": { "id": "COMMAND-bd4ir1mblj", "kind": "NotFound", "message": "User could not be found" }, "http_method": "POST", "logging.googleapis.com/sourceLocation": { "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError", "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go", "line": 57 }, "message": "ID=COMMAND-bd4ir1mblj Message=User could not be found\ngoroutine 1165 [running]:\nruntime/debug.Stack()\n\t/usr/lib/go/src/runtime/debug/stack.go:26 +0x8e\ngithub.com/zitadel/zitadel/internal/zerrors.newZitadelError(0x5, {0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/zerror.go:139 +0x1df\ngithub.com/zitadel/zitadel/internal/zerrors.ThrowNotFound({0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/not_found.go:6 +0x70\ngithub.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2(0xc000498588, {0xa3c1450, 0xc002cba540}, {0xc003030910, 0xb}, {0x0, 0x0}, {0xd550a80, 0x0, 0x0}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go:140 +0x711\ngithub.com/zitadel/zitadel/internal/api/grpc/user/v2.(*Server).DeleteUser(0xc003101500, {0xa3c1450, 0xc002cba540}, 0xc00216ec80)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/user/v2/user.go:191 +0x3f1\nconnectrpc.com/connect.NewUnaryHandler[...].func1({0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:51 +0x17e\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba540}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ActivityInterceptor.func1.1({0xa3c1450, 0xc002cba540}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/activity_interceptor.go:20 +0x1b1\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ServiceHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/service_interceptor.go:22 +0x283\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.validate({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80}, 0xc003033180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:35 +0x1bd\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ValidationHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ExecutionHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/execution_interceptor.go:39 +0x376\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.QuotaExhaustedInterceptor.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/quota_interceptor.go:25 +0x42d\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.TranslationHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/translation_interceptor.go:18 +0x94\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.authorize({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80}, 0xc0030331e0, {0xa3f0ab0, 0xc000203f10}, {{0xc000865980, 0x9, 0x9}}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:42 +0x894\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AuthorizationInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:17 +0x151\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.LimitsInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/limits_interceptor.go:31 +0x3b7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.toConnectError({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80}, 0xc003033200)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:21 +0x8a\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ErrorHandler.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AccessStorageInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/access_interceptor.go:21 +0x814\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.addInstanceByRequestedHost({0xa3c1450, 0xc001636630}, {0xa405eb0, 0xc00216ec80}, 0xc003033230, {0xa3a6428, 0xc00069f040}, 0xc002076910, {0xc002cef510, 0x9})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:107 +0xbc3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.setInstance({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80}, 0xc003033230, {0xa3a6428, 0xc00069f040}, {0xc002cef510, 0x9}, 0xc002076910, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:61 +0xb05\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.InstanceInterceptor.func1.1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:23 +0x147\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.NoCacheInterceptor.func1.1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/cache_interceptor.go:21 +0x351\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/backend/v3/instrumentation/logging.NewConnectInterceptor.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/backend/v3/instrumentation/logging/connect_handler.go:26 +0x27b\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.RegisterMetrics({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80}, 0xc003033270, {0xcf754f8, 0x3, 0x3}, {0xc000530f60, 0x4, ...})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:47 +0x3f3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.MetricsHandler.func1.1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:31 +0x111\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\nconnectrpc.com/otelconnect.(*Interceptor).WrapUnary.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected]/interceptor.go:145 +0x16d7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc001636450}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.CallDurationHandler.func1.1({0xa3c1450, 0xc001636450}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/call_interceptor.go:15 +0xab\nconnectrpc.com/connect.NewUnaryHandler[...].func2({0x7fb6100a8e60, 0xc002fa5680})\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:78 +0x4e2\nconnectrpc.com/connect.(*Handler).ServeHTTP(0xc0040768c0, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/go/pkg/mod/connectrpc.com/[email protected].1/handler.go:333 +0x870\ngithub.com/zitadel/zitadel/pkg/grpc/user/v2/userconnect.NewUserServiceHandler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/Repositories/zitadel/zitadel/pkg/grpc/user/v2/userconnect/user_service.connect.go:1951 +0x8e5\nnet/http.HandlerFunc.ServeHTTP(0xc00314a200, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/rs/cors.(*Cors).Handler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/go/pkg/mod/github.com/rs/[email protected].1/cors.go:289 +0x2fd\nnet/http.HandlerFunc.ServeHTTP(0xc0022cbdc0, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.RobotsTagHandler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/robots_tag_interceptor.go:12 +0x88\nnet/http.HandlerFunc.ServeHTTP(0xc001c8fae8, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.WithOrigin.func1.1({0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/origin_interceptor.go:27 +0x42a\nnet/http.HandlerFunc.ServeHTTP(0xc00048e540, {0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/gorilla/mux.(*Router).ServeHTTP(0xc00207c540, {0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/home/tim/go/pkg/mod/github.com/gorilla/[email protected]/mux.go:212 +0x2bb\ngolang.org/x/net/http2.(*serverConn).runHandler(0xc0025545a0, 0xc0025bf360, 0xc0023fd180, 0xc001703488)\n\t/home/tim/go/pkg/mod/golang.org/x/[email protected].0/http2/server.go:2424 +0x1b9\ncreated by golang.org/x/net/http2.(*serverConn).scheduleHandler in goroutine 620\n\t/home/tim/go/pkg/mod/golang.org/x/[email protected].0/http2/server.go:2359 +0x1df\n", "path": "/zitadel.user.v2.UserService/DeleteUser", "protocol": "connect", "reportLocation": { "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go", "lineNumber": 140, "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2" }, "request_id": "d5kgsu8edncaon6puvag", "service": "zitadel.user.v2.UserService", "severity": "WARNING", "time": "2026-01-15T16:57:45.341000592+01:00" } ``` </details> When the GCP error reporting handler is enabled without stack trace, the `message` field contains the error string, also as per formatting requirements. <details> <summary>GCP error report without stack trace</summary> ```json { "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent", "SpanID": "2b4157f874eddbf4", "TraceID": "c34f7404fc0d021e1868ccc687bf2995", "domain": { "instance_host": "localhost:8080", "protocol": "http" }, "duration": "52.061597ms", "error": { "id": "COMMAND-bd4ir1mblj", "kind": "NotFound", "message": "User could not be found" }, "http_method": "POST", "logging.googleapis.com/sourceLocation": { "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError", "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go", "line": 57 }, "message": "ID=COMMAND-bd4ir1mblj Message=User could not be found", "path": "/zitadel.user.v2.UserService/DeleteUser", "protocol": "connect", "reportLocation": { "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go", "lineNumber": 140, "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2" }, "request_id": "d5kf5tgedncaiajvls9g", "service": "zitadel.user.v2.UserService", "severity": "WARNING", "time": "2026-01-15T15:00:22.910899273+01:00" } ``` </details> # Additional Changes - Private constructor for zitadel errors so that the stack-depth is consistent for different ways of error creation. (Throw and Create variants) - Request logs are now always at info level. - Bump Go version 1.25, required for `sloggcp` # Additional Context - closes #11329 --------- Co-authored-by: Copilot <[email protected]>
219 lines
5.9 KiB
Go
219 lines
5.9 KiB
Go
package instrumentation
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"connectrpc.com/connect"
|
|
"github.com/rs/xid"
|
|
slogmulti "github.com/samber/slog-multi"
|
|
slogctx "github.com/veqryn/slog-context"
|
|
slogotel "github.com/veqryn/slog-context/otel"
|
|
"github.com/zitadel/sloggcp"
|
|
"go.opentelemetry.io/contrib/bridges/otelslog"
|
|
"go.opentelemetry.io/otel/sdk/log"
|
|
"google.golang.org/grpc"
|
|
|
|
http_util "github.com/zitadel/zitadel/internal/api/http"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type LogFormat int
|
|
|
|
//go:generate enumer -type=LogFormat -trimprefix=LogFormat -text -linecomment -transform=snake
|
|
const (
|
|
// Empty line comment sets empty string of unspecified value
|
|
LogFormatUndefined LogFormat = iota //
|
|
LogFormatDisabled
|
|
LogFormatText
|
|
LogFormatJSON
|
|
// JSON formatted logs compatible with Google Cloud Platform
|
|
LogFormatGCP
|
|
LogFormatGCPErrorReporting
|
|
)
|
|
|
|
type LogConfig struct {
|
|
Level slog.Level
|
|
Format LogFormat
|
|
AddSource bool
|
|
Errors ErrorConfig
|
|
Exporter ExporterConfig
|
|
}
|
|
|
|
type ErrorConfig struct {
|
|
ReportLocation bool
|
|
StackTrace bool
|
|
}
|
|
|
|
// setLogger configures the global slog logger.
|
|
// Logs are sent to [os.Stderr] and/or the [log.LoggerProvider].
|
|
//
|
|
// When present in the context, each line emitted contains:
|
|
// - Trace ID
|
|
// - Span ID
|
|
// - Service name
|
|
// - Sanitized request hosts from [http_util.DomainCtx]
|
|
// - Request details, such as method and path.
|
|
func setLogger(provider *log.LoggerProvider, cfg LogConfig) {
|
|
options := &slog.HandlerOptions{
|
|
AddSource: cfg.AddSource,
|
|
Level: cfg.Level,
|
|
}
|
|
var stdErrHandler slog.Handler
|
|
switch cfg.Format {
|
|
case LogFormatUndefined:
|
|
return // uses default slog logger
|
|
case LogFormatDisabled:
|
|
stdErrHandler = slog.DiscardHandler
|
|
case LogFormatText:
|
|
stdErrHandler = slog.NewTextHandler(os.Stderr, options)
|
|
case LogFormatJSON:
|
|
stdErrHandler = slog.NewJSONHandler(os.Stderr, options)
|
|
case LogFormatGCP:
|
|
options.ReplaceAttr = sloggcp.ReplaceAttr
|
|
stdErrHandler = slog.NewJSONHandler(os.Stderr, options)
|
|
case LogFormatGCPErrorReporting:
|
|
zerrors.GCPErrorReportingEnabled(true)
|
|
options.ReplaceAttr = replaceErrAttr
|
|
stdErrHandler = sloggcp.NewErrorReportingHandler(os.Stderr, options)
|
|
}
|
|
|
|
zerrors.EnableReportLocation(cfg.Errors.ReportLocation)
|
|
zerrors.EnableStackTrace(cfg.Errors.StackTrace)
|
|
|
|
stdErrHandler = slogctx.NewHandler(
|
|
stdErrHandler,
|
|
&slogctx.HandlerOptions{
|
|
Prependers: []slogctx.AttrExtractor{
|
|
domainExtractor,
|
|
requestExtractor,
|
|
slogotel.ExtractTraceSpanID,
|
|
slogctx.ExtractPrepended,
|
|
},
|
|
},
|
|
)
|
|
|
|
otelHandler := otelslog.NewHandler(
|
|
Name,
|
|
otelslog.WithLoggerProvider(provider),
|
|
)
|
|
|
|
logger := slog.New(slogmulti.Fanout(stdErrHandler, otelHandler))
|
|
logger.Info("structured logger configured", "config_level", cfg.Level, "format", cfg.Format)
|
|
slog.SetDefault(logger)
|
|
}
|
|
|
|
const (
|
|
ProtocolHttp = "http"
|
|
ProtocolConnect = "connect"
|
|
ProtocolGrpc = "grpc"
|
|
)
|
|
|
|
// SetHttpRequestDetails adds static details to each context aware log entry.
|
|
func SetHttpRequestDetails(ctx context.Context, service string, request *http.Request) context.Context {
|
|
now := time.Now()
|
|
return context.WithValue(ctx, ctxKey{}, &requestDetails{
|
|
protocol: ProtocolHttp,
|
|
service: service,
|
|
http_method: request.Method,
|
|
path: request.URL.Path,
|
|
requestID: xid.NewWithTime(now),
|
|
start: now,
|
|
})
|
|
}
|
|
|
|
// SetConnectRequestDetails adds static details to each context aware log entry.
|
|
func SetConnectRequestDetails(ctx context.Context, request connect.AnyRequest) context.Context {
|
|
now := time.Now()
|
|
spec := request.Spec()
|
|
return context.WithValue(ctx, ctxKey{}, &requestDetails{
|
|
protocol: ProtocolConnect,
|
|
service: serviceFromRPCMethod(spec.Procedure),
|
|
http_method: request.HTTPMethod(),
|
|
path: spec.Procedure,
|
|
requestID: xid.NewWithTime(now),
|
|
start: now,
|
|
})
|
|
}
|
|
|
|
func SetGrpcRequestDetails(ctx context.Context, info *grpc.UnaryServerInfo) context.Context {
|
|
now := time.Now()
|
|
return context.WithValue(ctx, ctxKey{}, &requestDetails{
|
|
protocol: ProtocolGrpc,
|
|
service: serviceFromRPCMethod(info.FullMethod),
|
|
http_method: http.MethodPost, // gRPC always uses POST
|
|
path: info.FullMethod,
|
|
requestID: xid.NewWithTime(now),
|
|
start: now,
|
|
})
|
|
}
|
|
|
|
type ctxKey struct{}
|
|
|
|
type requestDetails struct {
|
|
protocol string
|
|
service string
|
|
http_method string
|
|
path string
|
|
requestID xid.ID
|
|
start time.Time
|
|
}
|
|
|
|
func (r *requestDetails) attrs() []slog.Attr {
|
|
attrs := make([]slog.Attr, 0, 6)
|
|
if r.protocol != "" {
|
|
attrs = append(attrs, slog.String("protocol", r.protocol))
|
|
}
|
|
if r.service != "" {
|
|
attrs = append(attrs, slog.String("service", r.service))
|
|
}
|
|
if r.http_method != "" {
|
|
attrs = append(attrs, slog.String("http_method", r.http_method))
|
|
}
|
|
if r.path != "" {
|
|
attrs = append(attrs, slog.String("path", r.path))
|
|
}
|
|
if !r.requestID.IsZero() {
|
|
attrs = append(attrs, slog.String("request_id", r.requestID.String()))
|
|
}
|
|
if !r.start.IsZero() {
|
|
attrs = append(attrs, slog.Duration("duration", time.Since(r.start)))
|
|
}
|
|
return attrs
|
|
}
|
|
|
|
func serviceFromRPCMethod(fullMethod string) string {
|
|
parts := strings.Split(fullMethod, "/")
|
|
if len(parts) >= 2 {
|
|
return parts[1]
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
// domainExtractor sets the sanitized request hosts from [http_util.DomainCtx] to a log entry.
|
|
func domainExtractor(ctx context.Context, _ time.Time, _ slog.Level, _ string) []slog.Attr {
|
|
return []slog.Attr{
|
|
slog.Any("domain", http_util.DomainContext(ctx)),
|
|
}
|
|
}
|
|
|
|
// requestExtractor sets the request details from [requestDetails] to a log entry.
|
|
func requestExtractor(ctx context.Context, _ time.Time, _ slog.Level, _ string) []slog.Attr {
|
|
if r, ok := ctx.Value(ctxKey{}).(*requestDetails); ok {
|
|
return r.attrs()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// replaceErrAttr renames the "err" attribute to the Google Cloud Platform compatible error key.
|
|
func replaceErrAttr(groups []string, a slog.Attr) slog.Attr {
|
|
if len(groups) == 0 && a.Key == "err" {
|
|
a.Key = sloggcp.ErrorKey
|
|
}
|
|
return a
|
|
}
|