Usage

Generating access logs

Inject an instance of the interceptor when creating a gRPC server:

from concurrent import futures
import grpc
from grpc_accesslog import AccessLogInterceptor

interceptors = [AccessLogInterceptor()]
server = grpc.server(
   futures.ThreadPoolExecutor(max_workers=10),
   interceptors=interceptors
)

Or with an asyncio server:

from concurrent import futures
import grpc.aio
from grpc_accesslog import AsyncAccessLogInterceptor

interceptors = [AsyncAccessLogInterceptor()]
server = grpc.aio.server(
   futures.ThreadPoolExecutor(max_workers=10),
   interceptors=interceptors
)

The server interceptor includes a complete default configuration. By default logs will be generated with the following format:

[::1] [03/Apr/2021:17:19:41 +0000] /grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo OK 0 grpc-go/1.35.0
  • gRPC Peer IP

  • Request received timestamp

  • Full RPC service and method path

  • String representation of gRPC Status Code

  • Size (in bytes) of serialized response message

  • gRPC user-agent string from invocation metadata (if available)

Configuring log fields

The contents of the access log are configuable by providing a tuple of log handlers and a separator string. The output of each handler will be joined in order by the separator.

from grpc_accesslog import AccessLogInterceptor, handler

interceptor = AccessLogInterceptor(
   handlers=(
      handler.peer,
      handler.time_received(),
      handler.request,
      handler.status,
      handler.response_size,
      handler.user_agent,
   ),
   separator=" ",
)

Several handlers are built into the grpc_accesslog.handler module.

  • peer – gRPC client IP address

  • request – Full RPC service and method path

  • response_size – Size of serialized gRPC response message, in bytes

  • rtt_ms – Delta of time_received and time_complete, in milliseconds

  • status – String representation of gRPC status code

  • time_received(format) – Timestamp of received request, formatted with strftime with format

  • time_complete(format) – Timestamp of completed RPC execution, formatted with strftime with format

  • user_agent – gRPC user agent from invocation metadata

Writing custom handlers

An access log handler is simply a Callable that accepts a grpc_accesslog.LogContext as its single argument and returns a string. The LogContext exposes all information available to the server interceptor:

class LogContext(NamedTuple):
   """Data available to gRPC log handlers."""

   server_context: grpc.ServicerContext
   method_name: str
   request: Message
   response: Union[Message, Generator]
   start: datetime
   end: datetime

Custom handlers can be written easily and added to the handler list.

from grpc_accesslog import AccessLogInterceptor, LogContext

def custom_metadata(log_context: LogContext) -> str:
   for md in log_context.server_context.invocation_metadata():
      if md.key == "my_custom_field":
            return md.value

   return "-"

interceptor = AccessLogInterceptor(
   handlers=(custom_metadata,),
)