graypy-0.2.14/0000755000076600000240000000000012740762033014734 5ustar severbanesiustaff00000000000000graypy-0.2.14/graypy/0000755000076600000240000000000012740762033016247 5ustar severbanesiustaff00000000000000graypy-0.2.14/graypy/__init__.py0000644000076600000240000000030412716367722020366 0ustar severbanesiustaff00000000000000from graypy.handler import GELFHandler, WAN_CHUNK, LAN_CHUNK try: from graypy.rabbitmq import GELFRabbitHandler, ExcludeFilter except ImportError: pass # amqplib is probably not installed graypy-0.2.14/graypy/handler.py0000644000076600000240000001441312740761612020243 0ustar severbanesiustaff00000000000000import datetime import sys import logging import json import zlib import traceback import struct import random import socket import math from logging.handlers import DatagramHandler PY3 = sys.version_info[0] == 3 WAN_CHUNK, LAN_CHUNK = 1420, 8154 if PY3: data, text = bytes, str else: data, text = str, unicode class GELFHandler(DatagramHandler): """Graylog Extended Log Format handler :param host: The host of the graylog server. :param port: The port of the graylog server (default 12201). :param chunk_size: Message chunk size. Messages larger than this size will be sent to graylog in multiple chunks. Defaults to `WAN_CHUNK=1420`. :param debugging_fields: Send debug fields if true (the default). :param extra_fields: Send extra fields on the log record to graylog if true (the default). :param fqdn: Use fully qualified domain name of localhost as source host (socket.getfqdn()). :param localname: Use specified hostname as source host. :param facility: Replace facility with specified value. If specified, record.name will be passed as `logger` parameter. :param level_names: Allows the use of string error level names instead of numerical values. Defaults to False :param compress: Use message compression. Defaults to True """ def __init__(self, host, port=12201, chunk_size=WAN_CHUNK, debugging_fields=True, extra_fields=True, fqdn=False, localname=None, facility=None, level_names=False, compress=True): self.debugging_fields = debugging_fields self.extra_fields = extra_fields self.chunk_size = chunk_size self.fqdn = fqdn self.localname = localname self.facility = facility self.level_names = level_names self.compress = compress DatagramHandler.__init__(self, host, port) def send(self, s): if len(s) < self.chunk_size: DatagramHandler.send(self, s) else: for chunk in ChunkedGELF(s, self.chunk_size): DatagramHandler.send(self, chunk) def makePickle(self, record): message_dict = make_message_dict( record, self.debugging_fields, self.extra_fields, self.fqdn, self.localname, self.level_names, self.facility) packed = message_to_pickle(message_dict) frame = zlib.compress(packed) if self.compress else packed return frame class ChunkedGELF(object): def __init__(self, message, size): self.message = message self.size = size self.pieces = struct.pack('B', int(math.ceil(len(message) * 1.0/size))) self.id = struct.pack('Q', random.randint(0, 0xFFFFFFFFFFFFFFFF)) def message_chunks(self): return (self.message[i:i + self.size] for i in range(0, len(self.message), self.size)) def encode(self, sequence, chunk): return b''.join([ b'\x1e\x0f', self.id, struct.pack('B', sequence), self.pieces, chunk ]) def __iter__(self): for sequence, chunk in enumerate(self.message_chunks()): yield self.encode(sequence, chunk) def make_message_dict(record, debugging_fields, extra_fields, fqdn, localname, level_names, facility=None): if fqdn: host = socket.getfqdn() elif localname: host = localname else: host = socket.gethostname() fields = {'version': "1.0", 'host': host, 'short_message': record.getMessage(), 'full_message': get_full_message(record.exc_info, record.getMessage()), 'timestamp': record.created, 'level': SYSLOG_LEVELS.get(record.levelno, record.levelno), 'facility': facility or record.name, } if level_names: fields['level_name'] = logging.getLevelName(record.levelno) if facility is not None: fields.update({ '_logger': record.name }) if debugging_fields: fields.update({ 'file': record.pathname, 'line': record.lineno, '_function': record.funcName, '_pid': record.process, '_thread_name': record.threadName, }) # record.processName was added in Python 2.6.2 pn = getattr(record, 'processName', None) if pn is not None: fields['_process_name'] = pn if extra_fields: fields = add_extra_fields(fields, record) return fields SYSLOG_LEVELS = { logging.CRITICAL: 2, logging.ERROR: 3, logging.WARNING: 4, logging.INFO: 6, logging.DEBUG: 7, } def get_full_message(exc_info, message): return '\n'.join(traceback.format_exception(*exc_info)) if exc_info else message def add_extra_fields(message_dict, record): # skip_list is used to filter additional fields in a log message. # It contains all attributes listed in # http://docs.python.org/library/logging.html#logrecord-attributes # plus exc_text, which is only found in the logging module source, # and id, which is prohibited by the GELF format. skip_list = ( 'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename', 'funcName', 'id', 'levelname', 'levelno', 'lineno', 'module', 'msecs', 'message', 'msg', 'name', 'pathname', 'process', 'processName', 'relativeCreated', 'thread', 'threadName') for key, value in record.__dict__.items(): if key not in skip_list and not key.startswith('_'): message_dict['_%s' % key] = value return message_dict def smarter_repr(obj): """ convert JSON incompatible object to string""" if isinstance(obj, datetime.datetime): return obj.isoformat() return repr(obj) def message_to_pickle(obj): """ convert object to a JSON-encoded string""" obj = sanitize(obj) serialized = json.dumps(obj, separators=',:', default=smarter_repr) return serialized.encode('utf-8') def sanitize(obj): """ convert all strings records of the object to unicode """ if isinstance(obj, dict): return dict((sanitize(k), sanitize(v)) for k, v in obj.items()) if isinstance(obj, (list, tuple)): return obj.__class__([sanitize(i) for i in obj]) if isinstance(obj, data): obj = obj.decode('utf-8', errors='replace') return obj graypy-0.2.14/graypy/rabbitmq.py0000644000076600000240000001026212716367722020434 0ustar severbanesiustaff00000000000000import json from amqplib import client_0_8 as amqp from graypy.handler import make_message_dict from logging import Filter from logging.handlers import SocketHandler try: from urllib.parse import urlparse, unquote except ImportError: from urlparse import urlparse from urllib import unquote _ifnone = lambda v, x: x if v is None else v class GELFRabbitHandler(SocketHandler): """RabbitMQ / Graylog Extended Log Format handler NOTE: this handler ingores all messages logged by amqplib. :param url: RabbitMQ URL (ex: amqp://guest:guest@localhost:5672/). :param exchange: RabbitMQ exchange. Default 'logging.gelf'. A queue binding must be defined on the server to prevent log messages from being dropped. :param debugging_fields: Send debug fields if true (the default). :param extra_fields: Send extra fields on the log record to graylog if true (the default). :param fqdn: Use fully qualified domain name of localhost as source host (socket.getfqdn()). :param exchange_type: RabbitMQ exchange type (default 'fanout'). :param localname: Use specified hostname as source host. :param facility: Replace facility with specified value. If specified, record.name will be passed as `logger` parameter. """ def __init__(self, url, exchange='logging.gelf', debugging_fields=True, extra_fields=True, fqdn=False, exchange_type='fanout', localname=None, facility=None, virtual_host='/'): self.url = url parsed = urlparse(url) if parsed.scheme != 'amqp': raise ValueError('invalid URL scheme (expected "amqp"): %s' % url) host = parsed.hostname or 'localhost' port = _ifnone(parsed.port, 5672) virtual_host = virtual_host if not unquote(parsed.path[1:]) else unquote(parsed.path[1:]) self.cn_args = { 'host': '%s:%s' % (host, port), 'userid': _ifnone(parsed.username, 'guest'), 'password': _ifnone(parsed.password, 'guest'), 'virtual_host': virtual_host, 'insist': False, } self.exchange = exchange self.debugging_fields = debugging_fields self.extra_fields = extra_fields self.fqdn = fqdn self.exchange_type = exchange_type self.localname = localname self.facility = facility self.virtual_host = virtual_host SocketHandler.__init__(self, host, port) self.addFilter(ExcludeFilter('amqplib')) def makeSocket(self, timeout=1): return RabbitSocket(self.cn_args, timeout, self.exchange, self.exchange_type) def makePickle(self, record): message_dict = make_message_dict( record, self.debugging_fields, self.extra_fields, self.fqdn, self.localname, self.facility) return json.dumps(message_dict) class RabbitSocket(object): def __init__(self, cn_args, timeout, exchange, exchange_type): self.cn_args = cn_args self.timeout = timeout self.exchange = exchange self.exchange_type = exchange_type self.connection = amqp.Connection( connection_timeout=timeout, **self.cn_args) self.channel = self.connection.channel() self.channel.exchange_declare( exchange=self.exchange, type=self.exchange_type, durable=True, auto_delete=False, ) def sendall(self, data): msg = amqp.Message(data, delivery_mode=2) self.channel.basic_publish(msg, exchange=self.exchange) def close(self): try: self.connection.close() except Exception: pass class ExcludeFilter(Filter): def __init__(self, name): """Initialize filter. Initialize with the name of the logger which, together with its children, will have its events excluded (filtered out). """ if not name: raise ValueError('ExcludeFilter requires a non-empty name') self.name = name self.nlen = len(name) def filter(self, record): return not (record.name.startswith(self.name) and ( len(record.name) == self.nlen or record.name[self.nlen] == ".")) graypy-0.2.14/graypy.egg-info/0000755000076600000240000000000012740762033017741 5ustar severbanesiustaff00000000000000graypy-0.2.14/graypy.egg-info/dependency_links.txt0000644000076600000240000000000112740762033024007 0ustar severbanesiustaff00000000000000 graypy-0.2.14/graypy.egg-info/not-zip-safe0000644000076600000240000000000112716370060022165 0ustar severbanesiustaff00000000000000 graypy-0.2.14/graypy.egg-info/PKG-INFO0000644000076600000240000001727312740762033021050 0ustar severbanesiustaff00000000000000Metadata-Version: 1.0 Name: graypy Version: 0.2.14 Summary: Python logging handler that sends messages in GELF (Graylog Extended Log Format). Home-page: https://github.com/severb/graypy Author: Sever Banesiu Author-email: banesiu.sever@gmail.com License: BSD License Description: Installing ========== Using easy_install:: easy_install graypy Install with requirements for ``GELFRabbitHandler``:: easy_install graypy[amqp] Usage ===== Messages are sent to Graylog2 using a custom handler for the builtin logging library in GELF format:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Alternately, use ``GELFRabbitHandler`` to send messages to RabbitMQ and configure your Graylog2 server to consume messages via AMQP. This prevents log messages from being lost due to dropped UDP packets (``GELFHandler`` sends messages to Graylog2 using UDP). You will need to configure RabbitMQ with a 'gelf_log' queue and bind it to the 'logging.gelf' exchange so messages are properly routed to a queue that can be consumed by Graylog2 (the queue and exchange names may be customized to your liking):: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFRabbitHandler('amqp://guest:guest@localhost/%2F', 'logging.gelf') my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Tracebacks are added as full messages:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) try: puff_the_magic_dragon() except NameError: my_logger.debug('No dragons here.', exc_info=1) Configuration parameters ======================== GELFHandler: * **host** - the host of the graylog server. * **port** - the port of the graylog server (default 12201). * **chunk_size** - message chunk size. messages larger than this size will be sent to graylog in multiple chunks (default `1420`). * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host (socket.getfqdn()). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as *logger* parameter. * **level_names** - allows the use of string error level names instead in addition to their numerical representation. GELFRabbitHandler: * **url** - RabbitMQ URL (ex: amqp://guest:guest@localhost:5672/%2F). * **exchange** - RabbitMQ exchange. Default 'logging.gelf'. A queue binding must be defined on the server to prevent log messages from being dropped. * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host - socket.getfqdn(). * **exchange_type** - RabbitMQ exchange type (default `fanout`). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as `logger` parameter. Using with Django ================= It's easy to integrate ``graypy`` with Django's logging settings. Just add a new handler in your ``settings.py`` like this:: LOGGING = { ... 'handlers': { 'graypy': { 'level': 'WARNING', 'class': 'graypy.GELFHandler', 'host': 'localhost', 'port': 12201, }, }, 'loggers': { 'django.request': { 'handlers': ['graypy'], 'level': 'ERROR', 'propagate': True, }, }, } Custom fields ============= A number of custom fields are automatically added if available: * function * pid * process_name * thread_name You can disable these additional fields if you don't want them by adding an argument to the handler:: handler = graypy.GELFHandler('localhost', 12201, debugging_fields=False) graypy also supports additional fields to be included in the messages sent to Graylog2. This can be done by using Python's LoggerAdapter_ and Filter_. In general, LoggerAdapter makes it easy to add static information to your log messages and Filters give you more flexibility, for example to add additional information based on the message that is being logged. Example using LoggerAdapter_:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_adapter = logging.LoggerAdapter(logging.getLogger('test_logger'), { 'username': 'John' }) my_adapter.debug('Hello Graylog2 from John.') Example using Filter_:: import logging import graypy class UsernameFilter(logging.Filter): def __init__(self): # In an actual use case would dynamically get this (e.g. from memcache) self.username = "John" def filter(self, record): record.username = self.username return True my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.addFilter(UsernameFilter()) my_logger.debug('Hello Graylog2 from John.') Contributors: * Sever Banesiu * Daniel Miller .. _LoggerAdapter: http://docs.python.org/howto/logging-cookbook.html#using-loggeradapters-to-impart-contextual-information .. _Filter: http://docs.python.org/howto/logging-cookbook.html#using-filters-to-impart-contextual-information .. image:: https://d2weczhvl823v0.cloudfront.net/severb/graypy/trend.png :alt: Bitdeli badge :target: https://bitdeli.com/free Keywords: logging gelf graylog2 graylog udp amqp Platform: UNKNOWN graypy-0.2.14/graypy.egg-info/requires.txt0000644000076600000240000000002712740762033022340 0ustar severbanesiustaff00000000000000 [amqp] amqplib==1.0.2 graypy-0.2.14/graypy.egg-info/SOURCES.txt0000644000076600000240000000043312740762033021625 0ustar severbanesiustaff00000000000000LICENSE MANIFEST.in README.rst setup.cfg setup.py graypy/__init__.py graypy/handler.py graypy/rabbitmq.py graypy.egg-info/PKG-INFO graypy.egg-info/SOURCES.txt graypy.egg-info/dependency_links.txt graypy.egg-info/not-zip-safe graypy.egg-info/requires.txt graypy.egg-info/top_level.txtgraypy-0.2.14/graypy.egg-info/top_level.txt0000644000076600000240000000000712740762033022470 0ustar severbanesiustaff00000000000000graypy graypy-0.2.14/LICENSE0000644000076600000240000000274412716367722015761 0ustar severbanesiustaff00000000000000Copyright (c) 2011, Sever Băneşiu All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. graypy-0.2.14/MANIFEST.in0000644000076600000240000000004312716367722016500 0ustar severbanesiustaff00000000000000include LICENSE include README.rst graypy-0.2.14/PKG-INFO0000644000076600000240000001727312740762033016043 0ustar severbanesiustaff00000000000000Metadata-Version: 1.0 Name: graypy Version: 0.2.14 Summary: Python logging handler that sends messages in GELF (Graylog Extended Log Format). Home-page: https://github.com/severb/graypy Author: Sever Banesiu Author-email: banesiu.sever@gmail.com License: BSD License Description: Installing ========== Using easy_install:: easy_install graypy Install with requirements for ``GELFRabbitHandler``:: easy_install graypy[amqp] Usage ===== Messages are sent to Graylog2 using a custom handler for the builtin logging library in GELF format:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Alternately, use ``GELFRabbitHandler`` to send messages to RabbitMQ and configure your Graylog2 server to consume messages via AMQP. This prevents log messages from being lost due to dropped UDP packets (``GELFHandler`` sends messages to Graylog2 using UDP). You will need to configure RabbitMQ with a 'gelf_log' queue and bind it to the 'logging.gelf' exchange so messages are properly routed to a queue that can be consumed by Graylog2 (the queue and exchange names may be customized to your liking):: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFRabbitHandler('amqp://guest:guest@localhost/%2F', 'logging.gelf') my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Tracebacks are added as full messages:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) try: puff_the_magic_dragon() except NameError: my_logger.debug('No dragons here.', exc_info=1) Configuration parameters ======================== GELFHandler: * **host** - the host of the graylog server. * **port** - the port of the graylog server (default 12201). * **chunk_size** - message chunk size. messages larger than this size will be sent to graylog in multiple chunks (default `1420`). * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host (socket.getfqdn()). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as *logger* parameter. * **level_names** - allows the use of string error level names instead in addition to their numerical representation. GELFRabbitHandler: * **url** - RabbitMQ URL (ex: amqp://guest:guest@localhost:5672/%2F). * **exchange** - RabbitMQ exchange. Default 'logging.gelf'. A queue binding must be defined on the server to prevent log messages from being dropped. * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host - socket.getfqdn(). * **exchange_type** - RabbitMQ exchange type (default `fanout`). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as `logger` parameter. Using with Django ================= It's easy to integrate ``graypy`` with Django's logging settings. Just add a new handler in your ``settings.py`` like this:: LOGGING = { ... 'handlers': { 'graypy': { 'level': 'WARNING', 'class': 'graypy.GELFHandler', 'host': 'localhost', 'port': 12201, }, }, 'loggers': { 'django.request': { 'handlers': ['graypy'], 'level': 'ERROR', 'propagate': True, }, }, } Custom fields ============= A number of custom fields are automatically added if available: * function * pid * process_name * thread_name You can disable these additional fields if you don't want them by adding an argument to the handler:: handler = graypy.GELFHandler('localhost', 12201, debugging_fields=False) graypy also supports additional fields to be included in the messages sent to Graylog2. This can be done by using Python's LoggerAdapter_ and Filter_. In general, LoggerAdapter makes it easy to add static information to your log messages and Filters give you more flexibility, for example to add additional information based on the message that is being logged. Example using LoggerAdapter_:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_adapter = logging.LoggerAdapter(logging.getLogger('test_logger'), { 'username': 'John' }) my_adapter.debug('Hello Graylog2 from John.') Example using Filter_:: import logging import graypy class UsernameFilter(logging.Filter): def __init__(self): # In an actual use case would dynamically get this (e.g. from memcache) self.username = "John" def filter(self, record): record.username = self.username return True my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.addFilter(UsernameFilter()) my_logger.debug('Hello Graylog2 from John.') Contributors: * Sever Banesiu * Daniel Miller .. _LoggerAdapter: http://docs.python.org/howto/logging-cookbook.html#using-loggeradapters-to-impart-contextual-information .. _Filter: http://docs.python.org/howto/logging-cookbook.html#using-filters-to-impart-contextual-information .. image:: https://d2weczhvl823v0.cloudfront.net/severb/graypy/trend.png :alt: Bitdeli badge :target: https://bitdeli.com/free Keywords: logging gelf graylog2 graylog udp amqp Platform: UNKNOWN graypy-0.2.14/README.rst0000644000076600000240000001372712716367722016446 0ustar severbanesiustaff00000000000000Installing ========== Using easy_install:: easy_install graypy Install with requirements for ``GELFRabbitHandler``:: easy_install graypy[amqp] Usage ===== Messages are sent to Graylog2 using a custom handler for the builtin logging library in GELF format:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Alternately, use ``GELFRabbitHandler`` to send messages to RabbitMQ and configure your Graylog2 server to consume messages via AMQP. This prevents log messages from being lost due to dropped UDP packets (``GELFHandler`` sends messages to Graylog2 using UDP). You will need to configure RabbitMQ with a 'gelf_log' queue and bind it to the 'logging.gelf' exchange so messages are properly routed to a queue that can be consumed by Graylog2 (the queue and exchange names may be customized to your liking):: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFRabbitHandler('amqp://guest:guest@localhost/%2F', 'logging.gelf') my_logger.addHandler(handler) my_logger.debug('Hello Graylog2.') Tracebacks are added as full messages:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) try: puff_the_magic_dragon() except NameError: my_logger.debug('No dragons here.', exc_info=1) Configuration parameters ======================== GELFHandler: * **host** - the host of the graylog server. * **port** - the port of the graylog server (default 12201). * **chunk_size** - message chunk size. messages larger than this size will be sent to graylog in multiple chunks (default `1420`). * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host (socket.getfqdn()). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as *logger* parameter. * **level_names** - allows the use of string error level names instead in addition to their numerical representation. GELFRabbitHandler: * **url** - RabbitMQ URL (ex: amqp://guest:guest@localhost:5672/%2F). * **exchange** - RabbitMQ exchange. Default 'logging.gelf'. A queue binding must be defined on the server to prevent log messages from being dropped. * **debugging_fields** - send debug fields if true (the default). * **extra_fields** - send extra fields on the log record to graylog if true (the default). * **fqdn** - use fully qualified domain name of localhost as source host - socket.getfqdn(). * **exchange_type** - RabbitMQ exchange type (default `fanout`). * **localname** - use specified hostname as source host. * **facility** - replace facility with specified value. if specified, record.name will be passed as `logger` parameter. Using with Django ================= It's easy to integrate ``graypy`` with Django's logging settings. Just add a new handler in your ``settings.py`` like this:: LOGGING = { ... 'handlers': { 'graypy': { 'level': 'WARNING', 'class': 'graypy.GELFHandler', 'host': 'localhost', 'port': 12201, }, }, 'loggers': { 'django.request': { 'handlers': ['graypy'], 'level': 'ERROR', 'propagate': True, }, }, } Custom fields ============= A number of custom fields are automatically added if available: * function * pid * process_name * thread_name You can disable these additional fields if you don't want them by adding an argument to the handler:: handler = graypy.GELFHandler('localhost', 12201, debugging_fields=False) graypy also supports additional fields to be included in the messages sent to Graylog2. This can be done by using Python's LoggerAdapter_ and Filter_. In general, LoggerAdapter makes it easy to add static information to your log messages and Filters give you more flexibility, for example to add additional information based on the message that is being logged. Example using LoggerAdapter_:: import logging import graypy my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_adapter = logging.LoggerAdapter(logging.getLogger('test_logger'), { 'username': 'John' }) my_adapter.debug('Hello Graylog2 from John.') Example using Filter_:: import logging import graypy class UsernameFilter(logging.Filter): def __init__(self): # In an actual use case would dynamically get this (e.g. from memcache) self.username = "John" def filter(self, record): record.username = self.username return True my_logger = logging.getLogger('test_logger') my_logger.setLevel(logging.DEBUG) handler = graypy.GELFHandler('localhost', 12201) my_logger.addHandler(handler) my_logger.addFilter(UsernameFilter()) my_logger.debug('Hello Graylog2 from John.') Contributors: * Sever Banesiu * Daniel Miller .. _LoggerAdapter: http://docs.python.org/howto/logging-cookbook.html#using-loggeradapters-to-impart-contextual-information .. _Filter: http://docs.python.org/howto/logging-cookbook.html#using-filters-to-impart-contextual-information .. image:: https://d2weczhvl823v0.cloudfront.net/severb/graypy/trend.png :alt: Bitdeli badge :target: https://bitdeli.com/free graypy-0.2.14/setup.cfg0000644000076600000240000000013012740762033016547 0ustar severbanesiustaff00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 graypy-0.2.14/setup.py0000755000076600000240000000112512740761641016454 0ustar severbanesiustaff00000000000000#!/usr/bin/env python from setuptools import setup, find_packages setup( name='graypy', version='0.2.14', description="Python logging handler that sends messages in GELF (Graylog Extended Log Format).", long_description=open('README.rst').read(), keywords='logging gelf graylog2 graylog udp amqp', author='Sever Banesiu', author_email='banesiu.sever@gmail.com', url='https://github.com/severb/graypy', license='BSD License', packages=find_packages(), include_package_data=True, zip_safe=False, extras_require={'amqp': ['amqplib==1.0.2']}, )