Welcome to talisker’s documentation!

Contents:

Talisker - an opinionated WSGI app platform

https://img.shields.io/pypi/v/talisker.svg https://img.shields.io/travis/canonical-ols/talisker.svg Documentation Status Python code quality (LGTM) LGTM alerts

Talisker is an enhanced runtime for your WSGI application that aims to provide a common operational platform for your python microservices.

It integrates with many standard python libraries to give you out-of-the-box logging, metrics, error reporting, status urls and more.

Python version support

Talisker 0.20.0 was the last to support Python 2.7. Talisker version >=0.21.0 only supports Python 3.5, 3.6, 3.8 and 3.10, as they come with Ubuntu LTS releases.

Quick Start

Simply install Talisker with Gunicorn via pip:

pip install talisker[gunicorn]

And then run your WSGI app with Talisker (as if it was regular gunicorn).:

talisker.gunicorn app:wsgi -c config.py ...

This gives you 80% of the benefits of Talisker: structured logging, metrics, sentry error handling, standardised status endpoints and more.

Note: right now, Talisker has extensive support for running with Gunicorn, with more WSGI server support planned.

Elevator Pitch

Talisker integrates and configures standard python libraries into a single tool, useful in both development and production. It provides:

  • structured logging for stdlib logging module (with grok filter)

  • gunicorn as a wsgi runner

  • request id tracing

  • standard status endpoints

  • statsd/prometheus metrics for incoming/outgoing http requests and more.

  • deep sentry integration

It also optionally supports the same level of logging/metrics/sentry integration for:

  • celery workers

  • general python scripts, like cron jobs or management tasks.

Talisker is opinionated, and designed to be simple to use. As such, it is not currently very configurable. However, PR’s are very welcome!

For more information, see The Documentation, which should be found at:

https://talisker.readthedocs.io

Overview

Goals

Talisker is designed with the following high level goals:

  • to provide a standard platform for running wsgi apps

  • to support both operational and developer workflows in the same tool

  • to be easy as possible to integrate with any wsgi app and framework

The original motivation was to standardise tooling across a number of different wsgi services, and improve the status quo along the way. Some of the inspiration came from Finagle, Twitter’s service framework for the JVM, particularly the operational standardisation it provides.

In particular, we wanted to standardise how we monitor applications in production, including logging, metrics, errors and alerts. Talisker provides a single library to do this for all our apps, that our operations tooling can configure easily, and means the application doesn’t need to care about it.

Also we wanted to provide best practice features for applications to use. So we added support for structured logging, integrated with the python stdlib logging. This allows developers to add custom tags to their logs, as well as add operational tags. We also provide easy to use helpers for best practice usage of things like statsd clients and requests sessions, which were used inconsistently across our projects, with repeated performance problems.

FAQ

Some questions that have actually been asked, if not particularly frequently.

  1. Why does talisker use a custom entry point? Wouldn’t it be better to just be some WSGI middleware?

    There are 3 reasons for using a talisker specific entry point

    1. to configure stdlib logging early, before any loggers are created

    2. to allow for easy configuration of gunicorn for logging, statsd and other things.

    3. to do things like automatically wrap your wsgi in app in some simple standard middleware, to provide request id tracing and other things.

    If it was just middleware, logging would get configured too late for gunicorn’s logs to be affected, and you would need to add explicit middleware and config to your app and its gunicorn config. Doing it as an alternate entry point means you literally just switch out gunicorn for talisker, and you are good to go.

  2. Why just gunicorn? Why not twistd, or waitress, etc?

    Simply because we use gunicorn currently. Integrating with other wsgi application runners is totally possible, twistd support is in the works, with uwsgi support on the road map.

  3. Why is it called talisker?

    ‘WSGI’ sort of sounds like ‘whisky’ if you say it quick. One of my favourite whiskies is Talisker, I’ve even visited the distillery on the Isle of Skye. Also, Talisker is a heavily peated malt whisky, which is not to everyone’s taste, which seemed to fit thematically with a WSGI runtime that is also very opinionated and probably not to everyone’s taste. Also, it has 8 characters just like gunicorn, and it wasn’t taken on PyPI.

Development using Talisker

Talisker has been designed with the goal of working in development and production. This is to try and encourage the same tool used throughout.

Talisker’s default configuration is designed for production usage, e.g.:

  • only INFO level logs and above go to stderr

  • python’s warning system is disabled

DEVEL Mode

If the DEVEL env var is set, Talisker will run in DEVEL mode.

What this means varies on which tool you are using, but at a base level it enables python’s warning logs, as you generally want these in development.

For Gunicorn, DEVEL mode means a few more things:

  • sets timeout to 99999, to avoid timeouts when debugging

  • it enables auto reloading on code changes

Also, for developer convenience, if you manually set Gunicorn’s debug level to DEBUG, when in DEVEL mode, Talisker will actually log debug level messages to stderr.

Development Logging

Talisker logs have been designed to be readable in development.

This includes:

  • preserving the common first 4 fields in python logging for developer familiarity.

  • tags are rendered most-specific to least specific. This means that the tags a developer is interested in are likely first.

  • if stderr is an interactive tty, then logs are colorized, to aid human reading.

Colored Output

If in DEVEL mode, and stdout is a tty device, then Talisker will colorise log output.

To disable this, you can set the env var:

TALISKER_COLOR=no

or

TERM=dumb

The colorscheme looks best on dark terminal backgrounds, but should be readable on light terminals too.

If your terminal doesn’t support bold, dim, or italic text formatting, it might look unpleasent. In that case, you can try the simpler colors

TALISKER_COLOR=simple

Request Context

Talisker uses an implicit context to track requests during execution. It does this via the contextvars module from the Python standard library in Python 3.7+, falling back to the contextvars backport from PyPI. It also includes a minimal backport of ContextVar for use with Python 2.

Talisker creates a new context for each WSGI request or Celery job, and tracks the request id and other data in that context, such as timeout data. Asyncio is supported, either by the native support in python 3.7, or via the aiocontextvars package, which you can install by using the asyncio extra:

pip install talisker[asyncio]

Note: you need at least python 3.5.3+ to use asyncio with contextvars - aiocontextvars does not work on earlier versions.

Talisker also explicitly supports contexts when using the Gevent or Eventlet Gunicorn workers, by swapping the thread local storage out for the relative greenlet based storage. This support currently does not work in python 3.7 or above, as it is not possible to switch the underlying storage in the stdlib version of the contextvars library.

Request Id

One of the key elements of the context is to track the current request id. This id can be supplied via a the X-Request-Id header, or else a uuid4 is used.

This id is automatically attached to all log messages emitted during the request, as well as the detailed log message talisker emits for the request itself.

Talisker also support propagating this request id wherever possible. When using Talisker’s requests support, the current request id will be included in the outgoing request headers. When queuing celery jobs, the current request id will be passed as a header for that job, and then used by the job for all log messages when the job runs.

This allows deep tracing of a particular request id across multiple services boundaries, which is key to debugging complex issues in distributed systems.

Context API

Talisker exposes a public API for the current context.

from talisker import Context

Context.request_id          # get/set current request id
Context.clear()             # clear the current context
Context.new()               # create a new context

# you can also add extras to the current logging context

Context.logging.push(foo=1)

# or

with Context.logging(bar=2):
    ...

Testing with Talisker

Talisker provides various tools to assist in unit testing application that use it’s features.

Test suite integration

In order to run your tests in the same configuration as production, you should initialise Talisker for testing as early as possible in your test suite:

talisker.testing.configure_testing()

This will set up Talisker logging with a logging.NullHandler on the root logger, as well as configure the global sentry client to point to dummy remote to capture sentry messages.

Talisker uses some module globals, thread locals, and request contexts to store state. For unit tests, it is a good idea to ensure this state is cleared between tests, or else tests can not be properly isolated. To do so, ensure the following is run before every test:

talisker.testing.clear_all()

Test Helpers

Talisker provides a context manager for testing that will capture every log message, sentry report and statsd metric generated while it is active. It produces a test context that can be used to assert against these artefacts:

with talisker.testing.TestContext() as ctx:
    # code under test

The context object collects events that happened while active, and presents them for inspection:

# ctx.statsd is an array of statsd metrics as strings
self.assertEqual(ctx.statsd[0], 'some.metric:1.000000|ms')

# ctx.sentry is an array of sentry messages sent, as the JSON dict that was
# sent by the sentry client
self.assertEqual(ctx.sentry[0]['message'] == 'my message')

# ctx.logs is a talisker.testing.LogRecordList, which is essentially a list
# of logging.LogRecords
self.assertTrue(ctx.logs[0].msg == 'my msg')

Asserting against log messages is not simple, expecially with extra dicts, so the context provides some helpers.

For the most common cases of checking that something was logged:

# ctx.assert_log will assert that a log message exists, with a helpful
# AssertionError message if it does not
ctx.assert_log(
    name='app.logger',
    level='info',
    msg='my msg',
    extra={'foo': 'bar'},
)

The context.logs attribute also provides additional APIs: filter(), exists() and find():

my_logs = ctx.logs.filter(name='app.logger')
self.assertEqual(len(my_logs) == 2)
self.assertTrue(my_logs.exists(
    level='info',
    msg='my msg',
    extra={'foo': 'bar'},
))
warning = my_logs.find(level='warning')
self.assertIn('baz', warning.extra)

These APIs will search logs that match the supplied keyword arguments, using the keyword to look up the attribute on the logging.LogRecord instances. A full list of such attributes can be found here:

https://docs.python.org/3/library/logging.html#logrecord-attributes

For all these APIs, the following applies:

  • The ‘level’ keyword can be a case insensitive string or an int (e.g. ‘info’ or logging.INFO), and the appropriate LogRecord attribute (levelname or levelno) will be used.

  • The ‘msg’ keyword is compared against the raw message. The ‘message’ keyword is compared against the interpolated msg % args.

  • Matching for strings is contains, not equality, i.e. needle in haystack, not just needle == haystack.

  • The extra dict is special cased: each supplied extra key/value is checked against the LogRecord.extra dict that Talisker adds.

  • assert_log(…) raises an AssertionError if the log does not exist. The error message includes how many matches each supplied term independently matched, to help narrow down issues.

  • filter(…) returns a LogRecordList of matching logs, so is chainable.

  • find(…) returns the first LogRecord found, or None.

  • exists(…) returns True if a matching LogRecord is found, else False

Configuration

Talisker can be configured by the environment variables listed below. These variables can also be supplied in a python file, although environment variables override any file configuration.

TALISKER_CONFIG

A path to a python file containing configuration variables.

Note: this will only have effect when set via environment variable, for obvious reasons.

DEVEL

Allows coloured logs, warnings, and other development convieniences.

DEVEL mode enables coloured log output, enables python warnings and, for gunicorn, it sets longer timeouts and auto reloads by default.

TALISKER_COLOUR

Controls the coloured output of logs. Defaults to on if stdin is a tty. Can be set to: 0 (off), 1 (on), or ‘simple’ for a simpler colourscheme. Requires DEVEL mode to be enabled.

Can also be disabled with TERM=dumb env var.

DEBUGLOG

Path to write debug level logs to, which is enabled if path is writable.

Debug logs are rotated every 24h to limit size, with only a single 24 hour archive is kept.

TALISKER_SLOWQUERY_THRESHOLD

Set the threshold (in ms) over which SQL queries will be logged. Defaults to -1 (off). The queries are sanitised, and thus safe to ship to a log aggregator.

Setting to 0 will log every query, which can be useful in development. The queries are sanitized by not including the bind parameter values.

TALISKER_EXPLAIN_SQL

Include EXPLAIN plans in sql sentry breadcrumbs. Defaults to false.

When enabled, this will issue extra queries to the db when generating sentry reports. The EXPLAIN queries will be quick to execute, as it doesn’t actually run the query, but still, caution is advised.

TALISKER_SOFT_REQUEST_TIMEOUT

Set the threshold (in ms) over which WSGI requests will report a soft time out to sentry. Defaults to -1 (off).

A soft timeout is simply a warning-level sentry report for the request. The aim is to provide early warning and context for when things exceed some limit.

Note: this can be set on a per-endpoint basis using the talisker.request_timeout decorator.

TALISKER_REQUEST_TIMEOUT

Set a deadline for all requests. Any network requests that talisker suports (requests, psycopg2) will have their timeouts set to this deadline.

Note: this can be set on a per-endpoint basis using the talisker.request_timeout decorator.

TALISKER_LOGSTATUS

Sets whether http requests to /_status/ endpoints are logged in the access log or not. Defaults to false.

These log lines can add a lot of noise, so they are turned off by default.

TALISKER_NETWORKS

Sets additional CIDR networks that are allowed to access restricted /_status/ endpoints. Comma separated list.

This protection is very basic, and can be easily spoofed by X-Forwarded-For headers. You should ensure that your HTTP front end server is configuring these correctly before passing on to gunicorn.

TALISKER_STATUS_INTERFACE

Which network interface to respond to /_status requests on.

TALISKER_REVISION_ID

Sets the explicit revision of the application. If not set, a best effort detection of VCS revision is used.

This is used to tag sentry reports, as well as returned as a header and from /_status/ping.

The default lookup will try find a version-info.txt file, or git, hg, or bzr revno, and finally a setup.py version.

TALISKER_UNIT

Sets the instance name for use with sentry reports.

TALISKER_ENV

Sets the deployed environment for use with sentry reports (e.g. production, staging).

TALISKER_DOMAIN

Sets the site domain name for use with sentry reports.

STATSD_DSN

Sets the Statsd DSN string, in the form: udp://host:port/my.prefix

You can also add the querystring parameter ?maxudpsize=N, to change from the default of 512.

SENTRY_DSN

Sets the sentry DSN, as per usual sentry client configuration.

See the sentry DSN documentation for more details.

TALISKER_SANITISE_KEYS

Comma separated list of keys with sensitive information. Data in these keys is masked in logging and sentry reports.

Note that sentry’s default set of password keys are already included - this is for extra keys.

TALISKER_ID_HEADER

Header containing request id. Defaults to X-Request-Id.

TALISKER_DEADLINE_HEADER

Header for request deadline. Defaults to X-Request-Deadline.

Logging

Talisker configures a specific carefully-designed logging set up. At a high level, it configures the following with the stdlib logging module:

  • logger class that can collect structured data

  • formatter that supports structured data via logfmt

  • root handler that logs everything at INFO level to stderr

Talisker also provides additional debugging options that can be used for more information.

The stdlib logging module is old, based on a Java API, and makes heavy use of global state. Some of the implementation details for talisker are work arounds for these limitations. But stdlib logging is everywhere, and is actually very flexible, so we can make it work how we want to.

Configuring Logging

If you use Talisker’s entry points, like talisker.gunicorn or talisker.celery, then logging will be configured by default.

You can also use the more generic entrypoints that talisker provides to run any script with Talisker logging enabled.

talisker.run script.py ...

or

python -m talisker script.py ...

If you are using your own entry point, you can configure Talisker with:

import talisker
talisker.initialise()

Note that this should be done before any loggers are created via logging.getLogger() or similar, as they will not be altered.

This will set up logging, taking into account environment variables like DEBUGLOG. If you want to configure those parameters explcitly, you can do:

config = {'devel': True, 'debuglog': '/path'}
talisker.logs.configure(config)

For testing, you can use special test configuration, which sets up talisker style logging, except adds a single NullHandler to the root logger:

talisker.logs.configure_test_logging()

This means logging will work as per talisker’s set up, but you will get no log output. You can use the helpers in talisker.testing to help testing log messages.

Logger Class

Talisker sets a custom base logger class via logging.setLoggerClass(). Its only difference from logger.Logger is that it supports more explicitly storing ‘extra’ arguments to the log call. This allows the StructuredFormatter class to append an arbitrary number of flags to the formatted message. Without this, there is no way to know which fields of a LogRecord are supposed to be added as tags.

It also supports pulling in additional extra data from the current context, which is primarily used for providing request_id data for the log message.

Root Handler

Talisker simply adds a handler to the root logger to log to stderr, at the INFO log level.

  • Simple, self-contained application, no log file config

  • No file permissions needed by app

  • System handles buffering, synchronisation, persistence, rotation and shipping

  • Works in development

  • PaaS friendly

Root Logger Limitations

If you log messages against the root logger object (i.e. logging.getLogger()), then Talisker’s structured logging enhancements will not work. This is due to the fact that the stdlib logging module instanciates the root logger object at import, and has no facility for safely switching it to be another instance or class. For libraries, it is not recommended practice to use the root logger, you should be specific about your library’s top level logger anyway.

Debug Logging

Talisker also supports adding an additional root handler that logs to disk at DEBUG level. The stderr logging output is unchanged.

To enable, just set the DEBUGLOG envvar to the path you want the log file to go to:

DEBUGLOG=/path/to/logfile talisker ...

If talisker can open that file, it will add a handler to log to it at DEBUG level, and log a message at the start of your log output to say it is doing do. If it cannot open that file, it will log a message saying so, but not fail. The handler is a TimedRotatingFileHandler, set to 24 hour period with no backup copies, i.e. logs last for 24 hours at most.

This is designed to support development and production use cases.

In development, typically usage of DEBUG logs is by grepping a file, rather than viewing in the console, given the verbosity. So we write to disk where the developer has told us to, and they can grep/view the file there.

In production, operators sometimes want to turn on more logging for limited period, to debug a specific problem. But we generally don’t want to ship that extra logging. This is in part due to scaling - debug logs can be 10x more verbose than INFO, this could lead to a 10x traffic spike on your log aggregation service. Additionally, debug logs often include details that are sensitive, and that you don’t want stored centrally. So this mechanism of writing to a temporary log file helps in that scenario too, as the INFO logging on stderr that is shipped is unchanged.

Log Format

Talisker uses a default format that is designed to be human readable in development, but still structured for richer data.

The talisker logging format is as follows:

format = '%(asctime)s.%(msecs)03dZ %(levelname)s %(name)s "%(message)s"'
datefmt = "%Y-%m-%d %H:%M:%S"

which should look like this:

2016-07-14 01:02:03.456Z INFO app "hello"

This provides:

  • the default data python logging usually has

  • a more ISOish timestamp (uses . for msecs rather than , but we omit the T for readability)

  • explicit UTC timestamps (logging module uses local time by default /o)

  • explicitly quoted message (embedded ” are escaped)

Talisker can also append an arbitrary number of ‘tags’ on the end of the log line, following the logfmt idea. e.g.:

2016-07-14 01:02:03.456Z INFO app "hello" foo=bar baz="some value"

These extra tags can be specified in 2 main ways:

  1. By the developer at the call site:

      logger.info('something happened', extra={'foo': 'bar', 'context': 'I can haz it'})
    
    would output::
    
      2016-01-13 10:24:07.357Z INFO app "something happened" foo=bar, svc.context="I can haz it"
    
  2. For a specific context, e.g. for a request. Talisker uses this to add request_id to every log message for a specific request. e.g.:

    logger.info('something happened')
    

    would output:

    2016-01-13 10:24:07.357Z INFO app "something happened" request_id=<request id>
    

    You can add your own temporary context variables with a context manager:

    with talisker.logs.logging_context(foo="bar"):
        logger.info('my important message')
    

    would output:

    2016-01-13 10:24:07.357Z INFO app "my important message" foo=bar
    

Additionally, it would be expected that your log shipper should add additional tags, like hostname or service group, to the logfmt tags when shipping.

If there are any global or context keys, these will take precedence if there is a collision with developer supplied keys. The developer keys will be suffixed with a ‘_’ to preserve the info, with out stomping on the other keys.

Log Suppression

By default, talisker suppresses some loggers.

The python python py.warnings logger is set not to propagate, as these are just noise in production.

Additionally, talisker also configures the ‘requests’ logger to WARNING level. This is because the INFO level on requests is particularly verbose, and we use requests everywhere.

If you prefer to have full requests logs, you can simply set the level yourself.

e.g.:

logging.getLogger('requests').setLevel(logging.INFO)

Additional logging configuration

Talisker just sets a root handler with formatter. You are free to add your own additional loggers and handlers as needed via the normal methods, if you need to.

You can still benefit from the structured logging provided by talisker if you set your handler’s formatter to be an instance of talisker.logs.StructuredFormatter. This is a standard formatter, except it uses UTC for the time and adds the logfmt tags on the end. The default format is as specified in Log Format.

For example, suppose you want to enable debug logs for django’s db logger.

e.g:

handler = logging.FileHandler('db.log')
handler.setFormatter(talisker.logs.StructuredFormatter())
handler.setLevel(logging.DEBUG)
db = logging.getLogger('django.db.backends')
db.setLevel(logging.DEBUG)
db.setHandler(handler)

Gunicorn Logs and Configuration

When run via talisker.gunicorn, Gunicorn’s error logs use talisker’s logging.

Access logs are disabled by default, and are usually not needed when using Talisker, as it logs full structured log per request, which is a superset of the information in access logs. You can however still enable and configure gunicorn’s access logs as well if you wish to.

Talisker overrides some config options for gunicorn, mainly to do with logging. It issues warnings if the user specifies any of these configs, as they will not be applied. Specifically, the following gunicorn config items are ignored by the talisker.gunicorn runner:

  • –error-logfile, as talisker logs everything to stderr

  • –logger-class, talisker uses its custom class

  • –statsd-host and –statsd-port, as talisker uses the STATSD_DSN env var.

If you run talisker.gunicorn in devel mode, and specify –log-level=debug, it will output debug logs to the console.

Grok filters

Talisker includes a filter and patterns for parsing the logformat into logstash with grok. These are in the talisker/logstash/ directory of the source tree. They are also included in the python package as resources.

Gunicorn

Basic Usage

Gunicorn is the wsgi server used by Talisker. To use it, simply use the Talisker wrapper in place of regular gunicorn script.

$ talisker.gunicorn -c config.py myapp:wsgi_app

This wrapper simply initialises Talisker before passing control to Gunicorn’s entrypoint. As such, it takes exactly the same command line arguments, and behaves in the same way.

Talisker supports the sync, gevent, and eventlet workers. Others workers may work, but have not been tested. The only place it matters is in the context-local storage of Talsiker log tags and request ids. Talisker will use greenlet based contexts if it finds itself running in a greenlet context, or else a thread local object.

Python 3.6, Async and Requests

Due to changes in the SSL api in python 3.6, requests currently has a bug with https endpoints in monkeypatched async context. The details are at https://github.com/requests/requests/issues/3752, but basically the monkeypatching must be done before requests is imported. Normally, this would not affect gunicorn, as your app would only import requests in a worker process after the monkeypatch has been applied. However, because Talisker enables some integrations in the main process, before the gunicorn code is run, it triggers this bug. Specfically, we import the raven library to get early error handling, and raven imports requests.

We provide two special entrypoints to work around this problem, if you are using python 3.6 and eventlet or gevent workers with the requests library. They simply apply the appropriate monkeypatching first, before then just initialising talikser and running gunicorn as normal.

$ talisker.gunicorn.eventlet --worker-class eventlet -c config.py myapp:wsgi_app
$ talisker.gunicorn.gevent --worker-class gevent -c config.py myapp:wsgi_app

Sentry

Talisker provides out-of-the-box integration with sentry.

Specifically, Talisker adds:

  • some default configuration of the sentry client

  • handle WSGI errors

  • sentry error log handler (for logged exception messages)

  • log message, sql and http call breadcrumbs

  • sentry integration with flask, django, and celery

To get the current sentry client, simply use:

talisker.sentry.get_client()

Error Data

Talisker configures sentry breadcrumbs for log messages at INFO or higher level. It also adds the request id as a tag to the sentry message.

If you want to add some custom error context, you can use the client above as you would use the sentry client as normal. For example:

client = talisker.sentry.get_client()
client.context.merge({'tags': my_tags})

Sentry Configuration

Talisker uses the default SENTRY_DSN env var to configure sentry by default. Simply setting this will enable sentry for wsgi and logging.

In addition, Talisker configures the sentry client by default as follows:

  • sets install_logging_hook=False, as Talisker handles it

  • sets release to the current revision

  • sets hook_libraries=[], disabling breadcrumbs for request/httplib

  • sets environment to TALISKER_ENV envvar

  • sets name to TALISKER_UNIT envvar

  • sets site to TALISKER_DOMAIN envvar

  • ensures the RemovePostDataProcessor, SanitizePasswordsProcessor, and RemoveStackLocalsProcessor processors are always included, to be safe by default.

If you are using Talisker’s Flask or Django integration, you can configure your sentry client further via the usual config methods for those frameworks.

If you wish to manually configure the sentry client, use the following:

talisker.sentry.configure_client(**config)

This will reconfigure and reset the sentry client used by the wsgi middleware and logging handler that Talisker sets up.

If you want to set your own client instance, do:

talisker.sentry.set_client(client)

Whichever way you wish to configure sentry, talisker will honour your configuration except for 2 things

  1. install_logging_hook will always be set to false, or else you’ll get duplicate exceptions logged to sentry.

  2. the processors will always include the base 3, although you can add more. If you really need to remove the default processors, you can modify the list at talisker.sentry.default_processors and then call talisker.sentry.set_client().

Status Endpoints

Talisker provides a set of app-agnostic standard endpoints for your app for querying its status. This is designed so that in production you have standard ways to investigate problems, and to configure load balancer health checks and nagios checks.

/_status/ping

A simple check designed for use with haproxy’s httpcheck option, returns 200, responds to GET, HEAD, or OPTIONS, the body content being the application’s revision

/_status/check

For use with nagios check_http plugin, or similar.

It tries to hit /_status/check in your app. If that is not found, it just returns a 200, as a basic proxy for the application being up.

/_status/test/sentry (/_status/error for backwards compatibility)

Raise a test error, designed to test sentry/raven integration.

/_status/test/statsd

Send a test metric value. Designed to test statsd integration.

/_status/test/prometheus

Increment a test counter. Designed to test Prometheus integration.

/_status/metrics

Exposes prometheus metrics in Prometheus text format.

/_status/info/config

Shows the current Talisker configuration

/_status/info/packages

Shows a list of installed python packages and their versions

/_status/info/workers

Shows a summary of master and worker processes (e.g CPU, memory, fd count) and other process information. Only available if psutil is installed.

/_status/info/objgraph

Shows the most common python objects in user for the worker that services the request. Only available if objgraph is installed.

/_status/info/logtree

Displays the stdlib logging configuration using logging_tree. Only available if logging_tree is installed.

Request Timeouts

Talisker supports the idea of a request deadline, with the goal of failing early, especially when under load. This deadline can be specified as a timeout, either globaly or per-endpoint.

Talisker will try to use the remaining time left until the deadline as network timeout parameters. It supports HTTP and SQL requests out of the box, if you use talisker.requests.TaliskerAdapter and talisker.postgresql.TaliskerConnecton, respectivley. It also provides an API to get the remaining time left before the deadline, which you can use in other network operations.

timeout = Context.deadline_timeout()

Note: this will raise talisker.DeadlineExceeded if the deadline has been exceeded.

Talisker timeouts are not hard guarantees - Talisker will not cancel your request. They merely try to ensure that network operations will fail earlier rather than blocking for long periods.

Deadline Propagation

The deadline can be set via a the X-Request-Deadline request header, as an ISO 8601 datestring. This will override the configured endpoint deadline, if any. Talisker’s requests support will also send the current deadline as a header in any outgoing requests. This allows API gateway services to communicate top-level request deadlines ina calls to other services.

Configuring Timeouts

You can set a global timeout via the TALISKER_REQUEST_TIMEOUT config, or per endpoint with the talisker.request_timeout decorator.

@talisker.request_timeout(3000)  # milliseconds
def view(request):
    ...

Soft Timeouts

Talisker supports the concept of a soft_timeout, which will send a sentry report if a request takes longer than the soft timeout threshold. This is useful to provide richer information for problematic requests.

You can set this global via the TALISKER_SOFT_REQUEST_TIMEOUT config or per endpoint via the talisker.request_timeout decorator.

@talisker.request_timeout(soft_timeout=3000)  # milliseconds
def view(request):
    ...

Requests

Enhanced session

Talisker provides a way to upgrade a request.Session instance with a few extra features.

Firstly, the X-Request-Id header will be added to the outgoing request headers. This can be used by other services to track the originating request id. We usually append incoming request id to one generated for that request, e.g.:

X-Request-Id: <generated request id>-<incoming request id>.

This allows simple searches to uncover all related sub requests for a specific request, also known as fanout.

Secondly, we also collect metrics for outgoing requests. Specifically:

  • counter for all requests, broken down by host and view

  • counter for errors, broken down by host, type (http 5xx or connection error), view and error code (either POSIX error code or http status code, depending on type)

  • histogram for duration of http responses

In statsd, they would be named like so:

<prefix>.requests.count.<host>.<view>
<prefix>.requests.errors.<host>.<type>.<view>.<code>
<prefix>.requests.timeouts.<host>.<view>
<prefix>.requests.latency.<host>.<view>.<status>

Note: a view here is a human friendly name for the api/endpoint. If the upstream service returns an X-View-Name header in its response (e.g. is another talisker service), or if the user has given this call a name (see below), then this will be used.

You can customise the name of this metric if you wish, with some keyword arguments:

session.post(..., metric_api_name='myapi', metric_host_name='myservice')

will use these values in the resulting naming, in both prometheus and statsd.:

<prefix>.requests.count.myservice.myapi...

Session lifecycle

We found many of our services were not using session objects properly, often creating/destroying them per-request, thus not benefiting from the default connection pooling provided by requests. This is especially painful for latency when your upstream services are https, as nearly all ours are. But sessions are not thread-safe (see this issue for details), sadly, so a global session is risky.

So, talisker helps by providing a simple way to have thread local sessions.

Using a talisker session

To get a base requests.Session thread local session with metrics and request id tracing::

session = talisker.requests.get_session()

or use the wsgi environ:

session = environ['requests']

If you wish to use a custom subclass of Session rather than the default requests.Session, just pass the session class as an argument. Talisker will ensure there is one instance of this session subclass per thread.:

session = talisker.requests.get_session(MyCustomSessionClass)

This works because talisker does not subclass Session to add metrics or requests id tracing. Instead, it adds a response hook to the session object for metrics, and decorates the send method to inject the header (ugh, but I couldn’t find a better way).

If you wish to use talisker’s enhancements, but not the lifecycle management, you can do:

session = MySession()
talisker.requests.configure(session)

and session will now have metrics and id tracing.

Statsd

Talisker provides statsd integration and configuration.

Configuration

Statsd can be configured by the STATSD_DSN envvar, patterned after the SENTRY_DSN. This combines all statsd config into a single DSN url. For example:

.. code-block:: bash

# talk udp on port 1234 to host statsd, using a prefix of ‘my.prefix’ STATSD_DSN=udp://statsd:1234/my.prefix

# can also use / for prefix separators, the / converted to . STATSD_DSN=udp://statsd:1234/my/prefix

# ipv6 STATSD_DSN=udp6://statsd:1234/my.prefix

# custom max udp size of 1024 STATSD_DSN=udp://statsd:1234/my.prefix?maxudpsize=1024

Currently, only the udp statsd client is supported. If no config is provided, a dummy client is used that does nothing.

TODO: contribute this to upstream statsd module

Integration

If statsd is configured, talisker will configure gunicorn’s statsd functionality to use it. Additionally, it will enable statsd metrics for talisker’s requests sessions.

Your app code can get a statsd client by simply calling::

statsd = talisker.statsd.get_client()

Prometheus

Talisker provides optional prometheus_client integration and configuration.

Installation

The package supports extras args to install prometheus_client:

$ pip install talisker[prometheus]

Configuration

prometheus_client integration has extensive support for multiprocessing with gunicorn.

If you are only using one worker process, then regular single process mode is used.

However, if you have multiple workers, then the prometheus_multiproc_dir envvar is set to a tmpdir, as per the prometheus_client multiprocessing docs. This allows any worker being scraped to report metrics for all workers.

However, by default it leaks mmaped files when workers are killed, wasting disk space and slowing down metric collection. Talisker provides a non-trivial workaround for this, by having the gunicorn master merge left over metrics into a single file.

Note that in multiprocss mode, due to prometheus_client’s design, all registered metrics are exposed, regardless of registry.

The metrics are exposed at /_status/metrics

Celery

Talisker provides some optional integration with celery. If your project does not have celery installed, this will not be used.

To run a Celery worker with Talisker, use the provided Celery entrypoint:

$ talisker.celery worker -A myapp

Logging

Talisker configures Celery logging to use Talisker’s logging mechanisms. It ensures that the task_id and task_name will be logged with every log message for a Celery job.

Additionally, if the job is triggered by a Talisker process (e.g. a Talisker gunicorn worker) it will add the request_id to the logging tags for the celery job when it executes. This allows you to track tasks initiated by a specific request id.

Metrics

Talisker will enable basic celery task metrics by default.

Talisker sets up histogram metrics for

  • celery.<task_name>.enqueue (time to publish to queue)

  • celery.<task_name>.queue (time in queue)

  • celery.<task_name>.run (time to run task)

And counters for

  • celery.<task_name>.count (total tasks)

  • celery.<task_name>.retry (number of retried tasks)

  • celery.<task_name>.success (number of successful tasks)

  • celery.<task_name>.failure (number of failed tasks)

  • celery.<task_name>.revoked (number of revoked tasks)

Note: talisker supports celery>=3.1.0. If you need to be sure, the package supports extras args to install celery dependencies:

$ pip install talisker[celery]

Sentry

Talisker integrates Sentry with Celery, so Celery exceptions will be reported to Sentry. This uses the standard support in raven for integrating Celery and Sentry.

Flask

Usage

Talisker provides some opt-in support for flask apps. This does a few main things currently.

  1. enable sentry flask support for your app. This means you will get more information in your sentry errors, as well as being able to configure sentry via your app config as normal.

  2. disable flask default app logger configuration, and just use talisker’s configuration. This avoids double-logged exception messages.

  3. Add X-View-Name header to each response, which helps give extra logging and metric info.

To enable, you can either use a special Talisker flask app:

app = talisker.flask.TaliskerApp(__name__)

or register your app with Talisker afterwards:

talisker.flask.register(app)

Sentry Details

Talisker integrates the flask support in raven.contrib.flask. See the raven flask documentation for more details.

The sentry flask extension is configured to work with talisker.

  • logging=False as Talisker has already set this up. This means the other possible logging config is ignored.

  • wrap_wsgi=False as Talisker has already set this up

  • register_signal=True, which is the default

If for some reason you wish to configure the flask sentry extension yourself:

talisker.flask.sentry(app, **config)

This has the same api as the default raven.contrib.flask.Sentry object, but with the above configuration.

Django

Talisker provides opt-in support for Django apps.

Logging

To integrate with Talisker, you should at a minimum disable Django’s default logging in your settings.py:

LOGGING_CONFIG = None

If you don’t, you’ll get Django’s default logging configuration in addition to Talisker’s, leading to some duplicated logs in development, and possibly emails of errors in production, which is often not a good idea.

If you have custom logging you want to add on top of Talisker’s, you can follow the Django documentation for configuring logging yourself, with something like:

LOGGING = {...}
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(LOGGING)

which is exactly what Django does, but without the default logging.

Sentry

To integrate with Talisker’s sentry support, add raven to INSTALLED_APPS as normal, and also set SENTRY_CLIENT in your settings.py:

INSTALLED_APPS = [
    'raven.contrib.django.raven_compat',
    ...
]
SENTRY_CLIENT = 'talisker.django.SentryClient'

This will ensure the extra info Talsiker adds to Sentry messages will be applied, and also that the WSGI and logging handlers will use your Sentry configuration in settings.py. It will also set install_sql_hook=False, as that leaks raw SQL to the sentry server for every query. This will hopefully be addressed in a future release.

Metadata

Talisker supports the use of an X-View-Name header for better introspection. This is used for metric and logging information, to help debugging.

To support this in Django, simply add the following middleware, in any order:

MIDDLEWARE = [
    ...
    'talisker.django.middleware',
]

Management Tasks

If you use management tasks, and want them to run with Talisker logging, you can use the generic talisker runner:

talisker.run manage.py ...

or

python -m talisker manage.py ...

Postgresql

Talisker provides some optional integration with postgresql via pyscopg2, in the form of a custom connection/cursor implementation that integrates with logging and with sentry breadcrumbs.

Ensure you have the correct dependencies by using the pg extra:

pip install talisker[pg]

To use it in Django:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        ...,
        'OPTIONS': {
            'connection_factory': talisker.postgresql.TaliskerConnection
        }
    }
}

To use with sqlalchemy:

engine = sqlalchemy.create_engine(
    ...,
    connect_args={'connection_factory': talisker.postgresql.TaliskerConnection},
)

Query Security

Given the security sensitive nature of raw sql queries, Talisker is very cautious about what it might communicate externally, either via logs or via sentry.

Talisker will only ever log the query string with placeholders, and never the query parameters. This avoids leakage of sensitive information altogether, while still providing enough info to be useful to users trying to debug problems. If a query does not use query parameter, the query string is not sent, as there is no way to determine if it is sensitive or not.

One exception to this is stored procedures with parameters. The only access to the query is via the raw query that was actually run, which has already merged the query parameters, so we never send the raw query.

Note: in the future, we plan to add support for customised query sanitizing.

Slow query Logging

The connection logs slow queries to the talisker.slowqueries logger. The default timeout is -1, which disables slow query logging, but can be controlled with the TALISKER_SLOWQUERY_THRESHOLD env var. The value is measured in milliseconds:

export TALISKER_SLOWQUERY_THRESHOLD=100  # log queries over 100ms

Sentry Breadcrumbs

Talisker will capture all queries run during a request or other context as Sentry breadcrumbs. In the case of an error, they will include them in the report to sentry.

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

Note: this project is copyrighted by Canonical, and the policy is that contributers must sign the CLA in order to contribute. More details here: https://www.ubuntu.com/legal/contributors

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/canonical-ols/talisker/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.

  • Any details about your local setup that might be helpful in troubleshooting.

  • Detailed steps to reproduce the bug.

Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “feature” is open to whoever wants to implement it.

Write Documentation

talisker could always use more documentation, whether as part of the official talisker docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/canonical-ols/talisker/issues.

If you are proposing a feature:

  • Explain in detail how it would work.

  • Keep the scope as narrow as possible, to make it easier to implement.

  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up talisker for local development.

  1. Fork the talisker repo on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/talisker.git
    
  3. run make with no arguments. This will create a virtualenv env in .env and install dependencies and run tests.

  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  5. When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

    $ make  # runs python3 tests and lint
    $ make tox # runs all python tests and lint
    
  6. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  7. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.

  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.

  3. The pull request should work for Python 2.7, 3.4 and 3.5, and for PyPy. Check https://travis-ci.org/canonical-ols/talisker/pull_requests and make sure that the tests pass for all supported Python versions.

Credits

Release History

0.21.3 (2023-01-11)

  • Fix spelling of Python 3.5-specific requirements

  • Drop the future library

0.21.2 (2022-10-07)

  • Add support for python 3.10

  • Reduce test complexity by only supporting python versions that come with an ubuntu lts

  • Add support for newer Werkzeug

  • Pin jinja2 & markupsafe to sensible defaults for doc testing

0.21.1 (2021-11-10)

  • Add a fix to compare LogRecords that don’t contain the extra field i.e caused by log entries generated before talisker is initalized.

0.21.0 (2021-09-24)

  • Drop python 2.7

  • Support more django versions

  • Support more Celery versions

  • Add tests for python 3.9 and local tests for python 3.10

  • Remove travis tests

  • Remove the pg_wheel extra target

0.20.0 (2021-09-21)

  • Added support for python 3.7 and 3.8

  • Added support for celery => 5

  • Send more details (view-name) to sentry, for better grouping

  • Fix util.Local sorage thread safety, and include unittest

  • Last version to support python 2.7

0.19.0 (2020-09-07)

  • Fix proxy config application to backends in TalikerAdapter (#534)

  • Require contexts to be explicitly created. (#543)

Note: this change means that there is no default context. This fixes a number of issues with context existing in non-request/job code (e.g. startup code, gunicorn master process).

0.18.0 (2020-04-17)

  • Add support for Python 3.8 and gevent 1.5.0 (#515)

  • support latest prometheus-client, and raise minimum supported version to 0.5 (#516)

  • fix missing rogue import (#517)

  • Fix unbound local variable on request timeout. (#512)

0.17.0 (2020-04-08)

  • Fix the sdist builds so that they include setup.py /o

Sadly, many previous sdists are broken, and PyPI does not allow reuploading, so there is no real fix :(

0.16.1 (2020-04-02)

  • bumped supported version of werkzeug to <=1.2

0.16.0 (2020-03-04)

  • support gunicorn 20.x (#502)

  • Add EXPLAIN output to sql sentry breadcrumbs (#499)

  • Support X-Debug header that sends sentry report (#498)

  • Better gunicorn timeout handling. (#497)

  • Defer sentry reports until after request complete. (#496)

  • Sentry logging breadcrumb fixes (#493)

  • refactor wsgi request logging, so that we will have more information available (#492)

0.15.0 (2019-07-25)

  • support latest flask and werkzeug releases (#472)

  • Support per-request deadlines in network stack (#469)

  • Add TALISKER_STATUS_INTERFACE config to restrict bind to interface

  • Use contextvars for request context

0.14.3 (2019-04-24)

  • Fix lazy responses that have no content (#447)

  • Fix importing issues with talisker.run (#442)

0.14.2 (2019-03-29)

  • Change requests.TaliskerAdapter to require scheme (#435)

  • Do not assume requests Connection error always wraps a urllib3.Retry error. (#434)

0.14.1 (2019-03-21)

  • Fix no content responses by still trying to call start_response() early (#431)

0.14.0 (2019-03-18)

  • Make raven an optional dependency (#429)

  • properly support super lazy WSGI apps, like werkzeug’s debugger

0.13.0 (2019-03-08)

  • Make gunicorn an optional dependency

  • Fix use of werkzeug debugger

0.12.0 (2019-02-22)

This release includes a fair amount of internal refactoring, and some new features.

  • Logs and metrics for timeouts in gunicorn (#412)

  • Errors are now handled in WSGI, and return text/html/json per Accept header (#409)

  • Soft timeouts are sent after the request is finished (#411)

  • Add requests.TaliskerAdapter, a requests transport adapter with loadbalancing and retries (experimental, #405, #408)

  • Move logging from gunicorn into WSGI middleware (#402)

  • Don’t use raven’s WSGI middleware, do it in our middleware (#406)

  • All /_status/info/ endpoints support rendering JSON as well as text and HTML (#407)

  • support rendering json responses as well as text and html (#407)

  • move the request-id header name to config (#401)

  • fix requests latency buckets (#413)

0.11.1 (2019-01-09)

  • add additional sanitised keys by default, and add config for additional keys. Requires raven 6.4 (#392)

  • fix parsing of extra[‘start_time’] being a string in recent raven (#390)

0.11.0 (2018-12-18)

This release fixes an accidental hard dependency on celery that was added in 0.10.0.

The main feature is refactor of Talisker configuration, which now allows for using a config file as well as environment variables. It also provides consistant documentation for all configuration in a single place, and a new talisker.help cli tool, and a new /_status/info/config endpoint for introspection.

  • Add documentation for config, including new talisker.help command (#386)

  • Ensure optional extras are actually optional (#385)

  • Improve config error handling and logging of errors. (#382)

  • Add centralised config, with config file support (#380)

0.10.2 (2018-11-29)

  • Add companion TestContext.assert_not_log to match assert_log

0.10.1 (2018-11-26)

  • Add assert_log helper to TestContext (#377)

  • Fixed where some classes where accidentally old-style classes in python 2

0.10.0 (2018-11-20)

  • Add a public testing helper (testing.talisker.TestContext), to isolate and collect talisker data in tests (#329)

  • Expose new talisker.testing.clear_all() api to make it easier to reset talisker state in (#328)

  • Add a timeout for prometheus locks, and asupport non-blocking locking in gevent (#358)

  • Do not enable prometheus multiprocess mode if there is only one gunicorn worker (#358)

  • Remove username/email from any user data in sentry (#342)

  • Add a relative-to-request start timestamp to all sentry breadcrumbs. (#340)

  • Support prometheus 0.4+ (#334)

0.9.16 (2018-10-02)

  • Improve the ip->name mapping for requests metric names (#304)

  • improve sentry report metadata, add tags and also sql statement summary (#306)

  • Track per-request sql/http/log costs (#325)

  • add explicit context clears at various points (#305)

  • TERM=dumb turns off terminal colors

0.9.15 (2018-09-12)

  • Support statsd 3.3 (#290)

  • Properly serialize the prometheus worker cleanup (#296)

  • Gracefully fail if lock can’t be initialised (#303)

0.9.14 (2018-08-21)

A collection of minor improvements and fixes, and relicensing to Apache 2.

  • Relicense to Apache-2 (#258, #275) (thanks to Adam Collard)

  • Aggregate prometheus metrics into one file on worker death. (#281, #288)

  • Wrapping logging and metrics code with try/except, so that we never fail (#276)

  • Fail more gracefully in the case where talisker has not been setup properly (#264)

  • Add support for TALISKER_REVISION_ID variable (#262) (thanks to Robin Winslow and Adam Collard)

  • Return sentry id from capture (#261)

  • Set level=warning in soft req. timeout sentry messages (#255) (thanks to Guillermo Gonzalez)

0.9.13 (2018-07-04)

  • Fix X-Request-Id to be a native string on py2 (#247)

0.9.12 (2018-06-20)

  • only set prometheus envvar in gunicorn runner, not for all runners (#242)

0.9.11 (2018-06-18)

  • Fix a regression that broke prometheus metrics in multiprocess environments.

0.9.10 (2018-06-07)

  • Change default slow query threshold to -1 even in DEVEL (#226)

  • Move the clearing of various request contexts as late as possible (#233)

  • add soft request timeout support (#231) - thanks to Guillermo Gonzalez

  • support raven 6.9.0 (#232) - thanks to Guillermo Gonzalez

0.9.9 (2018-05-21)

This release collects a number of bugfixes

  • Make parsing the postgres dsn more robust (#224)

  • Protect django middleware view introspection (#220) (thanks to tomwardill)

  • Ensure that configured sentry processors is a set (#219) (thanks to tomwardill)

  • Fix link escaping in objgraph page (#217)

0.9.8 (2018-05-10)

The main feature in this release is new endpoints for debugging (#213):

  • /_status/info/packages: show a list of installed python packages and versions

  • /_status/info/workers: show resource usage of gunicorn workers, and general process infomation [requires psutil]

  • /_status/info/objgraph: show python object counts and potential leaks [requires objgraph]

These endpoints are IP restricted to TALISKER_NETWORKS, and can render as text via curl or html via browser.

  • Renamed all structured logging fields called “duration” to “duration_ms” to indicate units (#215)

  • Unknown /_status/ urls are passed through to app, to allow for 404 format control by the app (#212)

  • We only quote logfmt stings if needed, reduces visual noise on log lines (#173)

  • DEVEL colorscheme improved to support light terminals, and a simpler option that doesn’t use italics or bold (#188)

  • log request size/type in gunicorn logs (#174)

  • Added support for flask 1.0 and prometheus client 0.2, make django 1.10 the minimum supported version (#209, #198)

0.9.7 (2018-03-28)

The main work in this release has been improvments to metrics.

  • Collect prometheus metrics as well as statsd for gunicorn, requests, and celery (#172)

  • Support flask/django adding X-View-Name header to indicate view function (#151)

  • Control over naming requests metrics (#171)

  • Gunicorn logging enhancements (#165)

  • Gather better metadata from OSError exceptions

  • Fixed some small logging issues

0.9.6 (2018-02-21)

  • The logfmt output has been reworked to explictly quote strings, and test coverage much improved in the process. This allows for more robust parsing in logstash, such as allowing numeric fields.

  • New talisker.testing module, which has helpers for functionally testing talisker servers and related talisker tools.

  • Added a functional test suite using the new talisker.testing helpers

  • Custom ruby logstash filter to handle parsing of numeric values and escaped quotes.

0.9.5 (2017-05-23)

  • add support for postgresql via psycopg2 (#85). This will add breadcrumbs to sentry reports, as slow query logs. See http://talisker.readthedocs.io/en/latest/postgresql.html for more info

  • Access log cleanups (#94). We no longer include the querystring in the logmsg, just as a field.

  • Improved proxy handling in private endpoints. (#92). Fixes X-Forwarder-For handling.

  • Clear sentry context on request start (#90). This stops some breadcrumbs bleeding between requests.

  • Fix sentry default config when used with flask (#89). This was causing release tag to not be applied to sentry reports.

  • truncate long log messages (#86). This prevents DOSing logstash.

0.9.4 (2017-04-25)

  • require explicit DEVEL env var to enable colored output.

  • Add ability to force switch colors off with TALISKER_COLOR=no

  • Fix bug in grok filter to allow _ in logger name

  • Drop log tags that are empty, as logstash can’t cope with them

  • Truncate very long log messages and tags (at 10k/2k respectively) to avoid accidental DOS.

0.9.3 (2017-04-13)

  • Fix gunicorn logger metrics and logging, adding tests (#75)

0.9.2 (2017-04-11)

Bug fix release

  • Fix celery metrics with eager tasks (#70)

  • Fix statsd cli args and metric format (#71)

  • Also fix depencecies on recent setuptools

0.9.1 (2017-03-23)

This release has a couple of important bugfixes, upgrading is strongly encouraged.

  • Feature: Add a generic script runner to run any python script with talisker logging, primary usecase is django managment commands:

    talisker.run myscript.py …

  • Improvement: DEVEL env var is no longer required (although still respected). Talisker will assume DEVEL mode when stderr is a tty.

  • Bugfix: re-add http metrics for gunicorn which were accidentaly dropped in a refactor, with regression tests

  • Bugfix: fix celery integration with 3.1.13+, with regression tests

  • Bugfix: Add missing request_id to new accesslogs

  • Bugfix: Fix issue #35, respect –log-level for gunicorn in DEVEL mode. This means you can do –log-devel=debug and get debug level logging to your console.

  • Improvement: support raven 6

  • Testing: now testing against pypy in CI, and also agains the minimum supported versions of various dependencies too, to help prevent further accidental dependencies on latest version apis (which is what broke celery 3.1.x integration)

0.9.0 (2017-01-24)

The major feature in this release is support for sentry, which is integrated with wsgi, logging, and celery. Also supports opt-in integration with flask and django, see the relevant docs for more info.

Other changes

  • refactor of how logging contexts were implemented. More flexible and reliable. Note talisker.logs.extra_logging and talisker.logs.set_logging_context are now deprecated, you should use talisker.logs.logging_context and talisker.logs.logging_context.push, respectively, as covered in the updated logging docs.

  • improved celery logging, tasks logs now have task_id and task_name automatically added to their logs.

  • improved logging messages when parsing TALISKER_NETWORKS at startup

0.8.0 (2016-12-13)

  • prometheus: add optinal support for promethues_client

  • celery: request id automatically sent and logged, and support for 4.0

  • docs: initial ‘talisker contract’

  • statsd: better client initialisation

  • internal: refactoring of global variables, better /_status/ url dispatch

0.7.1 (2016-11-09)

  • remove use of future’s import hooks, as they mess with raven’s vendored imports

  • slight tweak to logfmt serialisation, and update docs to match

0.7.0 (2016-11-03)

Upgrading

This release includes a couple of minor backwards incompatible changes:

  1. access logs now use the talisker format, rather than CLF. See the docs for more info. If you are using access logs already, then the easiest upgrade path is to output the access logs to stderr (access_logfile=”-“), and delete your old log files.

  2. talisker no longer prefixes developer supplied tags with ‘svc.’. This should only matter if you’ve already set up dashboards or similar with the old prefixed name, and you will need to remove the prefix

Changes:

  • access logs now in logfmt rather than CLF

  • dummy statsd client is now useful in testing

  • logs are colored in development, to aid reading

  • the ‘svc’ prefix for tags has been removed

0.6.7 (2016-10-05)

  • actually include the encoding fix for check endpoint

0.6.6 (2016-10-05)

  • add celery metrics

  • fix issue with encoding in check endpoint when iterable

0.6.5 (2016-09-26)

  • make celery runner actually work, wrt logging

0.6.4 (2016-09-23)

  • fix encoding issue with X-Request-Id header (again!)

0.6.3 (2016-09-21)

  • fix setuptools entry points, which were typoed into oblivion.

0.6.2 (2016-09-21)

  • make gunicorn use proper statsd client

  • log some extra warnings if we try to configure gunicorn things that talisker overides.

  • better documented public api via __all__

  • first take on some celery helpers

  • some packaging improvements

0.6.1 (2016-09-12)

  • actually do remove old DEBUGLOG backups, as backupCount=0 does not remove any. Of course.

0.6.0 (2016-09-09)

  • Propagate gunicorn.error log, and remove its default handler.

This allows consistant logging, making the choice in all cases that your gunicorn logs go to the same stream as your other application log, making the choice in all cases that your gunicorn logs go to the same stream as your other application logs.

We issue a warning if the user tries to configure errorlog manually, as it won’t work as expected.

Indices and tables