Skip to content

Commit eace757

Browse files
committed
add custom logger level
1 parent e4991cd commit eace757

File tree

4 files changed

+128
-49
lines changed

4 files changed

+128
-49
lines changed

source/bridgeLogger.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
'''
2+
##############################################################################
3+
# Copyright 2019 IBM Corp.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
##############################################################################
17+
18+
Created on Mar 15, 2021
19+
20+
@author: HWASSMAN
21+
'''
22+
23+
import json
24+
import re
25+
import logging
26+
import sys
27+
import os
28+
29+
logging.TRACE = 5
30+
logging.addLevelName(logging.TRACE, 'TRACE')
31+
32+
logging.MOREINFO = 15
33+
logging.addLevelName(logging.MOREINFO,"MOREINFO")
34+
35+
class MyLogger(logging.getLoggerClass()):
36+
37+
def __init__(self, name, level=logging.NOTSET):
38+
super().__init__(name, level)
39+
40+
def trace(self, msg, *args, **kwargs):
41+
if self.isEnabledFor(logging.TRACE):
42+
self._log(logging.TRACE, msg, args, **kwargs)
43+
44+
def details(self, msg, *args, **kwargs):
45+
if self.isEnabledFor(logging.MOREINFO):
46+
self._log(logging.MOREINFO, msg, args, **kwargs)
47+
48+
49+
def configureLogging(logPath, logfile, loglevel=logging.INFO):
50+
51+
try:
52+
loglevel = logging._checkLevel(loglevel)
53+
except (ValueError, TypeError) as e:
54+
loglevel = logging.INFO
55+
56+
# create the logfile path if needed
57+
if not os.path.exists(logPath):
58+
os.makedirs(logPath)
59+
logfile = os.path.join(logPath, logfile)
60+
61+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)-8s - %(message)s')
62+
formatter1 = logging.Formatter('%(asctime)s - %(levelname)-8s - %(message)s', datefmt='%Y-%m-%d %H:%M')
63+
64+
# prepare the logger
65+
logging.setLoggerClass(MyLogger)
66+
logger = logging.getLogger(__name__)
67+
logger.setLevel(logging.TRACE)
68+
69+
strmhandler = logging.StreamHandler()
70+
strmhandler.setLevel(logging.INFO)
71+
strmhandler.setFormatter(formatter1)
72+
73+
rfhandler = logging.handlers.RotatingFileHandler(logfile, 'a', 1000000, 5) # 5 x 1M files
74+
rfhandler.setLevel(loglevel)
75+
rfhandler.setFormatter(formatter)
76+
77+
logger.addHandler(rfhandler)
78+
logger.addHandler(strmhandler)
79+
80+
logger.propagate = False # prevent propagation to default (console) logger
81+
return logger
82+

source/confParser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ def parse_cmd_args(argv):
101101
In this case make sure the \'query2port = \"9094\"\' is enabled in the ZIMon queryinterface settings (\'ZIMonCollector.cfg\')')
102102
parser.add_argument('-l', '--logPath', action="store", default="/var/log/ibm_bridge_for_grafana", help='location path of the log file (Default: /var/log/ibm_bridge_for_grafana')
103103
parser.add_argument('-f', '--logFile', action="store", default="zserver.log", help='Name of the log file (Default: zserver.log')
104-
parser.add_argument('-c', '--logLevel', action="store", type=int, default=logging.INFO, help='log level 10 (DEBUG), 20 (INFO), 30 (WARN), 40 (ERROR) (Default: 20)')
104+
parser.add_argument('-c', '--logLevel', action="store", type=int, default=15,
105+
help='log level. Available levels: 10 (DEBUG), 15 (MOREINFO), 20 (INFO), 30 (WARN), 40 (ERROR) (Default: 15)')
105106
parser.add_argument('-p', '--port', action="store", type=int, choices=[4242, 8443], default=4242, help='port number listening on for HTTP(S) connections (Default: 4242)')
106107
parser.add_argument('-t', '--tlsKeyPath', action="store", default=None, help='Directory path of tls privkey.pem and cert.pem file location (Required only for HTTPS port 8443)')
107108
parser.add_argument('-k', '--tlsKeyFile', action="store", default=None, help='Name of TLS key file, f.e.: privkey.pem (Required only for HTTPS port 8443)')

source/zimonGrafanaIntf.py

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from queryHandler import SensorConfig
3535
from __version__ import __version__
3636
from messages import ERR, MSG
37+
from bridgeLogger import configureLogging
3738
from confParser import getSettings
3839
from collections import defaultdict
3940
from timeit import default_timer as timer
@@ -82,10 +83,10 @@ def __initializeTables(self):
8283
foundItems = len(self.metaData.allParents) - 1
8384
sensors = self.metaData.sensorsSpec.keys()
8485
self.logger.info(MSG['MetaSuccess'])
85-
self.logger.info(MSG['ReceivAttrValues'].format('parents totally', foundItems))
86+
self.logger.details(MSG['ReceivAttrValues'].format('parents totally', foundItems))
8687
self.logger.debug(MSG['ReceivAttrValues'].format('parents', ", ".join(self.metaData.allParents)))
8788
self.logger.info(MSG['ReceivAttrValues'].format('sensors', ", ".join(sensors)))
88-
self.logger.info(MSG['TimerInfo'].format('Metadata', str(tend - tstart)))
89+
self.logger.details(MSG['TimerInfo'].format('Metadata', str(tend - tstart)))
8990

9091
def update(self):
9192
'''Read the topology from ZIMon and update
@@ -98,7 +99,7 @@ def update(self):
9899
if not (self.metaData and self.metaData.topo):
99100
self.logger.error(MSG['NoData']) # Please check the pmcollector is properly configured and running.
100101
raise cherrypy.HTTPError(404, MSG[404])
101-
self.logger.info(MSG['MetaSuccess'])
102+
self.logger.details(MSG['MetaSuccess'])
102103
self.logger.debug(MSG['ReceivAttrValues'].format('parents', ", ".join(self.metaData.allParents)))
103104
self.logger.debug(MSG['TimerInfo'].format('Metadata', str(tend - tstart)))
104105
return({'msg': MSG['MetaSuccess']})
@@ -252,14 +253,14 @@ def _getTimeMultiplier(self, timeunit):
252253
def _retrieveData(self, query, dsOp=None, dsInterval=None):
253254
'''Executes zimon query and returns results'''
254255

255-
self.logger.info(MSG['RunQuery'].format(query))
256+
self.logger.details(MSG['RunQuery'].format(query))
256257
tstart = timer()
257258
res = self.qh.runQuery(query)
258259
tend = timer()
259-
self.logger.info(MSG['TimerInfo'].format('runQuery: \"' + str(query) + '\"', str(tend - tstart)))
260+
self.logger.details(MSG['TimerInfo'].format('runQuery: \"' + str(query) + '\"', str(tend - tstart)))
260261
if res is None:
261262
return
262-
self.logger.info("res.rows length: {}".format(len(res.rows)))
263+
self.logger.details("res.rows length: {}".format(len(res.rows)))
263264
rows = res.rows
264265
if dsOp and dsInterval and len(res.rows) > 1:
265266
rows = res.downsampleResults(dsInterval, dsOp)
@@ -305,7 +306,7 @@ def _createZimonQuery(self, q, start, end):
305306
self.logger.error(MSG['MetricErr'].format(inMetric))
306307
raise cherrypy.HTTPError(404, MSG['MetricErr'].format(inMetric))
307308
else:
308-
self.logger.info(MSG['ReceivedQuery'].format(str(q), str(start), str(end)))
309+
self.logger.details(MSG['ReceivedQuery'].format(str(q), str(start), str(end)))
309310

310311
# add tagName or metric using the same method. There is no 'NOOP' option in openTSDB
311312
query.addMetric(inMetric, q.get('aggregator'))
@@ -342,13 +343,13 @@ def _createZimonQuery(self, q, start, end):
342343
dsBucketSize = self._calc_bucketSize(q.get('downsample'))
343344
if not dsOp and dsBucketSize > bucketSize:
344345
bucketSize = dsBucketSize
345-
self.logger.info(MSG['BucketsizeChange'].format(q.get('downsample'), bucketSize))
346+
self.logger.details(MSG['BucketsizeChange'].format(q.get('downsample'), bucketSize))
346347
elif dsBucketSize <= bucketSize:
347348
dsOp = dsInterval = None
348349
else:
349350
dsInterval = int(dsBucketSize / bucketSize)
350351
else:
351-
self.logger.info(MSG['BucketsizeToPeriod'].format(bucketSize))
352+
self.logger.details(MSG['BucketsizeToPeriod'].format(bucketSize))
352353

353354
query.setBucketSize(bucketSize)
354355

@@ -534,33 +535,13 @@ def processFormJSON(entity):
534535
cherrypy.serving.request.json = json.loads('{}')
535536

536537

537-
def configureLogging(logPath, logfile, loglevel):
538-
# create the logfile path if needed
539-
if not os.path.exists(logPath):
540-
os.makedirs(logPath)
541-
logfile = os.path.join(logPath, logfile)
542-
543-
# prepare the logger
544-
logger = logging.getLogger('zimonGrafanaIntf')
545-
rfhandler = logging.handlers.RotatingFileHandler(logfile, 'a', 1000000, 5) # 5 x 1M files
546-
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
547-
rfhandler.setFormatter(formatter)
548-
logger.addHandler(rfhandler)
549-
try:
550-
logger.setLevel(loglevel)
551-
except (ValueError, TypeError):
552-
logger.setLevel(logging.INFO)
553-
logger.propagate = False # prevent propagation to default (console) logger
554-
return logger
555-
556-
557538
def validateCollectorConf(args, logger):
558539

559540
# if not (args.get('server') == 'localhost') and not (args.get('server') == '127.0.0.1'):
560541
try:
561542
s = socket.socket()
562543
s.connect((args.get('server'), args.get('serverPort')))
563-
print(MSG['CollectorConnInfo'])
544+
logger.info(MSG['CollectorConnInfo'])
564545
finally:
565546
s.close()
566547
# else:
@@ -570,7 +551,7 @@ def validateCollectorConf(args, logger):
570551
# raise Exception("Invalid serverPort specified. Try with: %s" % str(foundPorts))
571552
# elif foundPorts[1] and not (args.get('serverPort') == int(foundPorts[1])):
572553
# args['serverPort'] = int(foundPorts[1])
573-
# logger.info(MSG['Query2port'].format(args['serverPort']))
554+
# logger.details(MSG['Query2port'].format(args['serverPort']))
574555

575556

576557
def updateCherrypyConf(args):
@@ -619,24 +600,21 @@ def main(argv):
619600

620601
# prepare metadata
621602
try:
622-
print("\n" + MSG['BridgeVersionInfo'].format(__version__))
623603
logger.info("%s", MSG['BridgeVersionInfo'].format(__version__))
624-
logger.info('zimonGrafanaItf invoked with parameters:\n %s', "\n".join("{}={}".format(k, v) for k, v in args.items()))
604+
logger.details('zimonGrafanaItf invoked with parameters:\n %s', "\n".join("{}={}".format(k, v) for k, v in args.items()))
625605
validateCollectorConf(args, logger)
626606
mdHandler = MetadataHandler(logger, args.get('server'), args.get('serverPort'))
627-
print(MSG['MetaSuccess'])
628-
print(MSG['ReceivAttrValues'].format('sensors', "\n\n" + "\t".join(mdHandler.metaData.sensorsSpec.keys())))
629607
except (AttributeError, ValueError, TypeError) as e:
630-
logger.exception('%s', MSG['IntError'].format(str(e)))
631-
print(MSG['MetaError'])
608+
logger.details('%s', MSG['IntError'].format(str(e)))
609+
logger.error(MSG['MetaError'])
632610
return
633611
except (Exception, IOError) as e:
634-
logger.exception('%s', MSG['IntError'].format(str(e)))
635-
print(MSG['CollectorErr'])
612+
logger.details('%s', MSG['IntError'].format(str(e)))
613+
logger.errort(MSG['CollectorErr'])
636614
return
637615
except (OSError) as e:
638-
logger.exception('%s', MSG['IntError'].format(str(e)))
639-
print("ZiMon sensor configuration file not found")
616+
logger.details('%s', MSG['IntError'].format(str(e)))
617+
logger.error("ZiMon sensor configuration file not found")
640618
return
641619

642620
ph = PostHandler(logger, mdHandler)
@@ -681,25 +659,25 @@ def main(argv):
681659
}
682660
)
683661

684-
print(MSG['sysStart'].format(sys.version, cherrypy.__version__))
685662
logger.info("%s", MSG['sysStart'].format(sys.version, cherrypy.__version__))
686663

687664
try:
688665
cherrypy.engine.start()
689-
print("server started")
666+
logger.info("server started")
667+
logger.debug("Server started PID: {}".format(os.getpid()))
690668
cherrypy.engine.block()
691-
except TypeError:
692-
print("Server request could not be proceed. Reason:")
669+
except TypeError as e:
670+
logger.error("Server request could not be proceed. Reason: {}".format(e))
693671
raise cherrypy.HTTPError(500, ERR[500])
694-
except IOError:
695-
print("STOPPING: Server request could not be proceed. Reason:")
672+
except OSError as e:
673+
logger.error("STOPPING: Server request could not be proceed. Reason: {}".format(e))
696674
cherrypy.engine.stop()
697675
cherrypy.engine.exit()
698676

699677
ph = None
700678
gh = None
701679

702-
print("server stopped")
680+
logger.warn("server stopped")
703681

704682

705683
if __name__ == '__main__':

tests/test_logger.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import logging
2+
from source.bridgeLogger import configureLogging
3+
from nose.tools import assert_raises_regexp, assert_raises
4+
5+
6+
def test_case01():
7+
with assert_raises(TypeError):
8+
configureLogging()
9+
10+
11+
def test_case02():
12+
with assert_raises(TypeError):
13+
configureLogging('/tmp')
14+
15+
16+
def test_case03():
17+
result = configureLogging('/tmp','mylog', 'abc')
18+
assert isinstance(result, logging.Logger)

0 commit comments

Comments
 (0)