Usage

Configure logging programmatically

import seqlog

seqlog.log_to_seq(
   server_url="http://my-seq-server:5341/",
   api_key="My API Key",
   level=logging.INFO,
   batch_size=10,
   auto_flush_timeout=10,  # seconds
   override_root_logger=True,
   json_encoder_class=json.encoder.JSONEncoder,  # Optional; only specify this if you want to use a custom JSON encoder
   support_extra_properties=True # Optional; only specify this if you want to pass additional log record properties via the "extra" argument.
)

For the best experience, use {x}-style named format arguments (passing those format arguments as keyword arguments to the log functions info, warning, error, critical, etc). Using unnamed “holes” (i.e. {}) is not currently supported.

For example:

logging.info("Hello, {name}!", name="World")

If you specify ordinal arguments, the log message is interpreted as a “%s”-style format string. The ordinal format arguments are stored in the log entry properties using the 0-based ordinal index as the property name.

logging.info("Hello, %s!", "World")

Note that mixing named and ordinal arguments is not currently supported.

The formal definition of the configure function is as follows:

seqlog.log_to_seq(server_url, api_key=None, level=30, batch_size=10, auto_flush_timeout=None, additional_handlers=None, override_root_logger=False, json_encoder_class=None, support_extra_properties=False, support_stack_info=False, ignore_seq_submission_errors=False, use_clef=False, **kwargs)[source]

Configure the logging system to send log entries to Seq.

Note that the root logger will not log to Seq by default.

Parameters
  • server_url – The Seq server URL.

  • api_key – The Seq API key (optional).

  • level – The minimum level at which to log.

  • batch_size – The number of log entries to collect before publishing to Seq.

  • auto_flush_timeout – If specified, the time (in seconds) before the current batch is automatically flushed.

  • additional_handlers – Additional `LogHandler`s (if any).

  • override_root_logger – Override the root logger, too? Note - this might cause problems if third-party components try to be clever when using the logging.XXX functions.

  • json_encoder_class – The custom JSONEncoder class (if any) to use. It not specified, the default JSONEncoder will be used.

  • support_extra_properties (bool) – Support passing of additional properties to log via the extra argument?

  • support_stack_info (bool) – Support attaching of stack-trace information (if available) to log records?

  • ignore_seq_submission_errors (bool) – Ignore errors encountered while sending log records to Seq?

  • use_clef (bool) – use more modern format to send events to Seq

Returns

The SeqLogHandler that sends events to Seq. Can be used to forcibly flush records to Seq.

Return type

SeqLogHandler

Configure logging from a file

Seqlog can also use a YAML-format file to describe the desired logging configuration. This file has the schema specified in Python’s logging.config module.

First, create your configuration file (e.g. /foo/bar/my_config.yml):

# This is the Python logging schema version (currently, only the value 1 is supported here).
version: 1

# Configure logging from scratch.
disable_existing_loggers: True

# Configure the root logger to use Seq
root:
  level: INFO
  handlers:
  - seq
  - console

# You can also configure non-root loggers.
loggers:
  another_logger:
      propagate: False
      level: INFO
      handlers:
      - seq
      - console

handlers:
# Log to STDOUT
  console:
    class: seqlog.structured_logging.ConsoleStructuredLogHandler
    formatter: seq

# Log to Seq
  seq:
    class: seqlog.structured_logging.SeqLogHandler
    formatter: seq

    # Seq-specific settings (add any others you need, they're just kwargs for SeqLogHandler's constructor).
    server_url: 'http://localhost:5341'
    api_key: 'your_api_key_if_you_have_one'

    # Use a custom JSON encoder, if you need to.
    json_encoder_class: json.encoder.JSONEncoder

formatters:
  seq:
    style: '{'

Then, call seqlog.configure_from_file():

seqlog.configure_from_file('/foo/bar/my_config.yml')

# Use the root logger.
root_logger = logging.getLogger()
root_logger.info('This is the root logger.')

# Use another logger
another_logger = logging.getLogger('another_logger')
another_logger.info('This is another logger.')

Configuring logging from a dictionary

Seqlog can also use a dictionary to describe the desired logging configuration. This dictionary has the schema specified in Python’s logging.config module.

config = {
  # configuration goes here
}

seqlog.configure_from_dict(config)

# Use the root logger.
root_logger = logging.getLogger()
root_logger.info('This is the root logger.')

# Use another logger
another_logger = logging.getLogger('another_logger')
another_logger.info('This is another logger.')

Batching and auto-flush

By default SeqLog will wait until it has a batch of 10 messages before sending them to Seq. You can control the batch size by passing a value for batch_size.

If you also want it to publish the current batch of events when not enough of them have arrived within a certain period, you can pass auto_flush_timeout (a float representing the number of seconds before an incomplete batch is published).

Overriding the root logger

By default, SeqLog does not modify the root logger (and so calls to logging.info() and friends do not support named format arguments). To also override the root logger, pass True for override_root_logger.

Additional LogHandlers

By default, log_to_seq only configures a single SeqLogHandler.

To configure additional LogHandlers, pass them via additional_handlers.

Global log properties

SeqLog can also add static properties to each log entry that is sent to Seq. By default, the following properties are added:

  • MachineName The local machine’s fully-qualified host name.

  • ProcessId The current process Id.

To configure global log properties, call set_global_log_properties, passing the properties as keyword arguments:

import seqlog

seqlog.set_global_log_properties(
    GlobalProperty1="foo",
    GlobalProperty2="bar"
    GlobalProperty3=26
)

Note that you can also clear the global log properties (so no properties are added) by calling clear_global_log_properties, and reset the global log properties to their defaults by calling reset_global_log_properties.

Note that is you specify a callable as part of global log properties, it will be called with no arguments right before logging:

import seqlog

def get_trace_id():
    if tracer.active_span is not None:
        return hex(tracer.active_span.context.trace_id)
    else:
        return None

seqlog.set_global_log_properties(
    trace_id=get_trace_id,
)

If the callable returns None, it won’t be added.

Note that some properties get different treatment if the CLEF mode is enabled.

Note that there is a short list of these, these won’t be attached to Properties. They will get removed from there and attached according to the `CLEF<https://clef-json.org/>`_ format:

  • span_id - this will get removed and be replaced with @sp

  • trace_id - this will get removed and be replaced with @tr

Callback on log submission failure

If you wish to set a callable to be invoked each time log submission fails, use the following function:

 from seqlog import set_callback_on_failure

 def handle_a_failure(e):    # type: (requests.RequestException) -> None
     print('Failure occurred during log submission: %s' % (e, ))

set_callback_on_failure(handle_a_failure)

The callable that you provide will accept a single positional argument, which is the requests exception instance that was the reason for the fail.

Note

This callable will be called only for I/O errors, errors stemming from seqlog not being able to convert your records into JSON won’t show up here!

Passing in exceptions

There’s a couple of ways you can pass in exception information. The only field sent to Seq will be called Exception and it may come from a couple of places, in order:

  1. A previously rendered exception was cached

  2. If FeatureFlag.STACK_INFO is enabled, stack_info is set and exc_info is not set, it will be taken from stack_info

  3. If exc_info is a tuple then
    1. If it’s first element is None, FeatureFlag.STACK_INFO is enabled and record.stack_info is available, stack_info will be used to construct the message

    2. Else formatted exc_info will be used

  4. If only exc_info is given, and it’s an Exception, then the stack trace will be attached.

  5. If exc_info is a string, it will be attached. This is functionally the same thing as (2).

So if you provide both exc_info and stack_info the code will behave in a way that’s hard to put into words.