1#
2# Copyright (C) 2010-2017 Vinay Sajip. See LICENSE.txt for details.
3#
4import logging
5
6class HTTPHandler(logging.Handler):
7    """
8    A class which sends records to a Web server, using either GET or
9    POST semantics.
10
11    :param host: The Web server to connect to.
12    :param url: The URL to use for the connection.
13    :param method: The HTTP method to use. GET and POST are supported.
14    :param secure: set to True if HTTPS is to be used.
15    :param credentials: Set to a username/password tuple if desired. If
16                        set, a Basic authentication header is sent. WARNING:
17                        if using credentials, make sure `secure` is `True`
18                        to avoid sending usernames and passwords in
19                        cleartext over the wire.
20    """
21    def __init__(self, host, url, method="GET", secure=False, credentials=None):
22        """
23        Initialize an instance.
24        """
25        logging.Handler.__init__(self)
26        method = method.upper()
27        if method not in ["GET", "POST"]:
28            raise ValueError("method must be GET or POST")
29        self.host = host
30        self.url = url
31        self.method = method
32        self.secure = secure
33        self.credentials = credentials
34
35    def mapLogRecord(self, record):
36        """
37        Default implementation of mapping the log record into a dict
38        that is sent as the CGI data. Overwrite in your class.
39        Contributed by Franz Glasner.
40
41        :param record: The record to be mapped.
42        """
43        return record.__dict__
44
45    def emit(self, record):
46        """
47        Emit a record.
48
49        Send the record to the Web server as a percent-encoded dictionary
50
51        :param record: The record to be emitted.
52        """
53        try:
54            import http.client, urllib.parse
55            host = self.host
56            if self.secure:
57                h = http.client.HTTPSConnection(host)
58            else:
59                h = http.client.HTTPConnection(host)
60            url = self.url
61            data = urllib.parse.urlencode(self.mapLogRecord(record))
62            if self.method == "GET":
63                if (url.find('?') >= 0):
64                    sep = '&'
65                else:
66                    sep = '?'
67                url = url + "%c%s" % (sep, data)
68            h.putrequest(self.method, url)
69            # support multiple hosts on one IP address...
70            # need to strip optional :port from host, if present
71            i = host.find(":")
72            if i >= 0:
73                host = host[:i]
74            h.putheader("Host", host)
75            if self.method == "POST":
76                h.putheader("Content-type",
77                            "application/x-www-form-urlencoded")
78                h.putheader("Content-length", str(len(data)))
79            if self.credentials:
80                import base64
81                s = ('u%s:%s' % self.credentials).encode('utf-8')
82                s = 'Basic ' + base64.b64encode(s).strip()
83                h.putheader('Authorization', s)
84            h.endheaders(data if self.method == "POST" else None)
85            h.getresponse()    #can't do anything with the result
86        except (KeyboardInterrupt, SystemExit):
87            raise
88        except:
89            self.handleError(record)
90