This repository has been archived by the owner on Sep 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from .formatters import FormatFactory | ||
from .handlers import start_logger | ||
|
||
__all__ = [ | ||
'FormatFactory', | ||
'start_logger', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import os | ||
import json | ||
import logging | ||
import datetime | ||
import traceback | ||
|
||
|
||
class FormatFactory: | ||
|
||
app_key = None | ||
service = None | ||
source = None | ||
env = None | ||
|
||
def __init__(self, app_key: str, source: str, service: str, env: str) -> None: | ||
self.app_key = app_key | ||
self.service = service | ||
self.source = source | ||
self.env = env | ||
self.host = os.uname().nodename | ||
|
||
def my_format(self, record: logging.LogRecord) -> str: | ||
data = { | ||
"timestamp": datetime.datetime.fromtimestamp( | ||
record.created, datetime.timezone.utc).strftime( | ||
"%Y-%m-%dT%H:%M:%S.%fZ"), | ||
"host": self.host, | ||
"ddsource": self.source, | ||
"syslog.env": self.env, | ||
"service": self.service, | ||
"message": record.getMessage(), | ||
"level": record.levelname, | ||
"logger.name": record.name, | ||
"pathname": record.pathname, | ||
"lineno": record.lineno, | ||
} | ||
# work with exception here | ||
ex_info = record.exc_info | ||
if ex_info is not None: | ||
(extype, value, tb) = ex_info | ||
data["error.message"] = str(value) | ||
data["error.kind"] = str(extype) | ||
data["error.stack"] = "".join(traceback.format_tb(tb)) | ||
return "{} {}".format(self.app_key, json.dumps(data)) | ||
|
||
def format(self, record: logging.LogRecord) -> str: | ||
# yeah python logging expects formatting of a message to also adjust | ||
# internal attributes of LogRecord - so sad | ||
# on the other hand record.message is the only thing that will matter | ||
# from here on | ||
record.message = self.my_format(record) | ||
return record.message |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import logging | ||
import socket | ||
import ssl | ||
import typing | ||
from queue import Queue | ||
|
||
|
||
def do_debug() -> bool: | ||
return False | ||
|
||
# reference to fuly grasp what is happening here | ||
# https://github.com/python/cpython/blob/master/Lib/logging/handlers.py | ||
|
||
|
||
class DogHandler(logging.handlers.SocketHandler): | ||
|
||
def __init__(self): | ||
# initialize parent with datadogs info | ||
# hardcoding is fine given that should this change | ||
# we likely need to fix this code anyway | ||
# gives less chance for users to mess things up | ||
# as well | ||
super().__init__("intake.logs.datadoghq.com", 10516) | ||
|
||
def makePickle(self, record): | ||
"""prepares record for writing over wire""" | ||
return "{}\n".format(record.message).encode("utf8") | ||
|
||
def send(self, s): | ||
"""sends serialized data s over wire""" | ||
if do_debug(): | ||
print("DOGDEBUG send", s) | ||
super().send(s) | ||
|
||
def makeSocket(self, timeout=1): | ||
""" | ||
A factory method which allows subclasses to define the precise | ||
type of socket they want. | ||
Since our logging data is sensitive we most def want to send it | ||
over SSL so we subclass and act accordingly | ||
""" | ||
context = ssl.create_default_context() | ||
|
||
# self.address should contain the host port tuple | ||
# that was initialized by the SocketHandler.__init__ | ||
# here we rely on inner workings of other peoples code | ||
# people who encourage us to do so while having no idea | ||
# what parts of their code we are depending on | ||
# but hey this is official python code and docs | ||
# surely they know what they are doing, right? | ||
# personally if any of this breaks in future versions of python | ||
# not least bit surprised | ||
# written and tested for python 3.6 | ||
s = socket.create_connection(self.address, timeout=timeout) | ||
conn = context.wrap_socket(s, server_hostname=self.host) | ||
if do_debug(): | ||
print("DOGDEBUG makeSocket connected {} to {} with {}".format( | ||
str(conn), str(self.address), conn.version())) | ||
return conn | ||
|
||
|
||
def start_logger(que: Queue) -> typing.Callable: | ||
""" | ||
this starts the QueueListener in separate thread | ||
that will just consume and forward all the messages to datadog | ||
so logging should never ever block | ||
and can be used by any high performance apps | ||
it returns a function which called will properly shut down the listener thread | ||
and close tcp sockets | ||
""" | ||
if do_debug(): | ||
print('Starting logger for queue', que) | ||
handler = DogHandler() | ||
listener = logging.handlers.QueueListener(que, handler) | ||
formatter = logging.Formatter("%(message)s") | ||
handler.setFormatter(formatter) | ||
listener.start() | ||
return listener.stop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import setuptools | ||
|
||
with open("README.md", "r") as fh: | ||
long_description = fh.read() | ||
|
||
setuptools.setup( | ||
name="datadoglog", | ||
version="0.0.1", | ||
author="Jakub Labath", | ||
author_email="[email protected]", | ||
description="Logging to datadog log", | ||
long_description=long_description, | ||
long_description_content_type="text/markdown", | ||
url="https://github.com/gadventures/datadoglog", | ||
packages=setuptools.find_packages(), | ||
classifiers=[ | ||
"Programming Language :: Python :: 3", | ||
"License :: OSI Approved :: Apache Software License", | ||
"Operating System :: OS Independent", | ||
], | ||
) |