EELF logging is added 43/46243/1
authorAnkitkumar Patel <ankit@research.att.com>
Fri, 4 May 2018 15:47:45 +0000 (11:47 -0400)
committerAnkitkumar Patel <ankit@research.att.com>
Fri, 4 May 2018 15:48:19 +0000 (11:48 -0400)
EELF logging is added.

Issue-ID: OPTFRA-227
Change-Id: I2ec7ab4c13f93736acee82a36b9420480d78b50c
Signed-off-by: Ankitkumar Patel <ankit@research.att.com>
16 files changed:
.gitignore
config/common_config.yaml
config/onap_logging_common_v1.config [new file with mode: 0755]
docker/Dockerfile
osdf/logging/onap_common_v1/CommonLogger.py [new file with mode: 0755]
osdf/logging/onap_common_v1/CommonLogger_test.config [new file with mode: 0755]
osdf/logging/onap_common_v1/CommonLogger_testing.py [new file with mode: 0755]
osdf/logging/onap_common_v1/README.md [new file with mode: 0755]
osdf/logging/onap_common_v1/__init__.py [new file with mode: 0755]
osdf/logging/onap_common_v1/makefile [new file with mode: 0755]
osdf/logging/osdf_logging.py
osdfapp.py
ssl_certs/oof.crt [new file with mode: 0644]
ssl_certs/oof.crt.pem [new file with mode: 0644]
ssl_certs/oof_new.key [new file with mode: 0644]
test/functest/simulators/simulated-config/onap_logging_common_v1.config [new file with mode: 0755]

index e1eb8cd..8d04c8e 100644 (file)
@@ -112,3 +112,8 @@ venv.bak/
 # pyCharm
 .idea/
 xunit*.xml
+
+# Autogenerated for simulations
+simulator-logs
+test/functest/simulators/config
+test/functest/simulators/osdf
index 5790042..5fab8a2 100644 (file)
@@ -5,7 +5,7 @@ osdf_system:
         external: 8698  # clients use this port on DockerHost
     osdf_ip_default: 0.0.0.0
 #        # Important Note: At deployment time, we need to ensure the port mapping is done
-#   ssl_context: ['./../etc/osdf_aaf.crt', './../etc/osdf_aaf.key']
+    ssl_context: ['./ssl_certs/oof.crt', './ssl_certs/oof_new.key']
 
 osdf_temp:  # special configuration required for "workarounds" or testing
     local_policies:
diff --git a/config/onap_logging_common_v1.config b/config/onap_logging_common_v1.config
new file mode 100755 (executable)
index 0000000..56f58d3
--- /dev/null
@@ -0,0 +1,58 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+# You may change this file while your program is running and CommonLogger will automatically reconfigure accordingly.
+# Changing these parameters may leave old log files lying around.
+
+
+#--- Parameters that apply to all logs
+#
+# rotateMethod:  time, size, stdout, stderr, none
+#... Note:  the following two parameters apply only when rotateMethod=time
+# timeRotateIntervalType:  S, M, H, D, W0 - W6, or midnight  (seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC)
+# timeRotateInterval:  >= 1  (1 means every timeRotateIntervalType, 2 every other, 3 every third, etc.)
+#... Note:  the following parameter applies only when rotateMethod=size
+# sizeMaxBytes:  >= 0  (0 means no limit, else maximum filesize in Bytes)
+# backupCount:  >= 0  (Number of rotated backup files to retain.  If rotateMethod=time, 0 retains *all* backups.  If rotateMethod=size, 0 retains *no* backups.)
+#
+rotateMethod           = time
+timeRotateIntervalType = midnight
+timeRotateInterval     = 1
+sizeMaxBytes           = 0
+backupCount            = 6
+
+
+#--- Parameters that define log filenames and their initial LogLevel threshold
+#... Note:  CommonLogger will exit if your process does not have permission to write to the file.
+#
+
+error           = logs/error.log
+errorLogLevel   = WARN
+errorStyle      = error
+
+metrics         = logs/metrics.log
+metricsLogLevel = INFO
+metricsStyle    = metrics
+
+audit           = logs/audit.log
+auditLogLevel   = INFO
+auditStyle      = audit
+
+debug           = logs/debug.log
+debugLogLevel   = DEBUG
+debugStyle      = debug
index 7a38ad8..78f7910 100644 (file)
@@ -50,4 +50,4 @@ ENV PATH /mz-dist:$PATH
 RUN git clone http://gerrit.onap.org/r/optf/osdf \
     && pip install --no-cache-dir -r osdf/requirements.txt
 
-CMD [ "/osdf/osdfapp.sh" ]
+CMD [ "source /osdf/osdfapp.sh" ]
diff --git a/osdf/logging/onap_common_v1/CommonLogger.py b/osdf/logging/onap_common_v1/CommonLogger.py
new file mode 100755 (executable)
index 0000000..6572d6f
--- /dev/null
@@ -0,0 +1,900 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+"""ONAP Common Logging library in Python."""
+
+#!/usr/bin/python
+# -*- indent-tabs-mode: nil -*- vi: set expandtab:
+
+
+from __future__ import print_function
+import os, sys, getopt, logging, logging.handlers, time, re, uuid, socket, threading
+
+class CommonLogger:
+    """ONAP Common Logging object.
+
+    Public methods:
+    __init__
+    setFields
+    debug
+    info
+    warn
+    error
+    fatal
+    """
+
+    UnknownFile = -1
+    ErrorFile = 0
+    DebugFile = 1
+    AuditFile = 2
+    MetricsFile = 3
+    DateFmt = '%Y-%m-%dT%H:%M:%S'
+    verbose = False
+
+    def __init__(self, configFile, logKey, **kwargs):
+        """Construct a Common Logger for one Log File.
+
+        Arguments:
+        configFile             -- configuration filename.
+        logKey                 -- the keyword in configFile that identifies the log filename.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages,
+                                       one of CommonLogger.ErrorFile, CommonLogger.DebugFile,
+                                       CommonLogger.AuditFile and CommonLogger.MetricsFile, or
+                                       one of the strings "error", "debug", "audit" or "metrics".
+                                       May also be set in the config file using a field named
+                                       <logKey>Style (where <logKey> is the value of the logKey
+                                       parameter). The keyword value overrides the value in the
+                                       config file.
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._monitorFlag = False
+
+        # Get configuration parameters
+        self._logKey = str(logKey)
+        self._configFile = str(configFile)
+        self._rotateMethod = 'time'
+        self._timeRotateIntervalType = 'midnight'
+        self._timeRotateInterval = 1
+        self._sizeMaxBytes = 0
+        self._sizeRotateMode = 'a'
+        self._socketHost = None
+        self._socketPort = 0
+        self._typeLogger = 'filelogger'
+        self._backupCount = 6
+        self._logLevelThreshold = self._intLogLevel('')
+        self._logFile = None
+        self._begTime = None
+        self._begMsec = 0
+        self._fields = {}
+        self._fields["style"] = CommonLogger.UnknownFile
+        try:
+            self._configFileModified = os.path.getmtime(self._configFile)
+            for line in open(self._configFile):
+                line = line.split('#',1)[0]    # remove comments
+                if '=' in line:
+                    key, value = [x.strip() for x in line.split('=',1)]
+                    if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none']:
+                        self._rotateMethod = value.lower()
+                    elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
+                        self._timeRotateIntervalType = value
+                    elif key == 'timeRotateInterval' and int( value ) > 0:
+                        self._timeRotateInterval = int( value )
+                    elif key == 'sizeMaxBytes' and int( value ) >= 0:
+                        self._sizeMaxBytes = int( value )
+                    elif key == 'sizeRotateMode' and value in ['a']:
+                        self._sizeRotateMode = value
+                    elif key == 'backupCount' and int( value ) >= 0:
+                        self._backupCount = int( value )
+                    elif key == self._logKey + 'SocketHost':
+                        self._socketHost = value
+                    elif key == self._logKey + 'SocketPort' and int( value ) == 0:
+                        self._socketPort = int( value )
+                    elif key == self._logKey + 'LogType' and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
+                        self._typeLogger = value.lower()
+                    elif key == self._logKey + 'LogLevel':
+                        self._logLevelThreshold = self._intLogLevel(value.upper())
+                    elif key == self._logKey + 'Style':
+                        self._fields["style"] = value
+                    elif key == self._logKey:
+                        self._logFile = value
+        except Exception as x:
+            print("exception reading '%s' configuration file: %s" %(self._configFile, str(x)), file=sys.stderr)
+            sys.exit(2)
+        except:
+            print("exception reading '%s' configuration file" %(self._configFile), file=sys.stderr)
+            sys.exit(2)
+
+        if self._logFile is None:
+            print('configuration file %s is missing definition %s for log file' %(self._configFile, self._logKey), file=sys.stderr)
+            sys.exit(2)
+
+
+        # initialize default log fields
+        # timestamp will automatically be generated
+        for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
+                    'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
+                    'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
+                    'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
+                    'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
+                    'errorDescription' ]:
+            if key in kwargs and kwargs[key] != None:
+                self._fields[key] = kwargs[key]
+
+        self._resetStyleField()
+
+        # Set up logger
+        self._logLock = threading.Lock()
+        with self._logLock:
+            self._logger = logging.getLogger(self._logKey)
+            self._logger.propagate = False
+            self._createLogger()
+
+        self._defaultServerInfo()
+
+        # spawn a thread to monitor configFile for logLevel and logFile changes
+        self._monitorFlag = True
+        self._monitorThread = threading.Thread(target=self._monitorConfigFile, args=())
+        self._monitorThread.daemon = True
+        self._monitorThread.start()
+
+
+    def _createLogger(self):
+        if self._typeLogger == 'filelogger':
+            self._mkdir_p(self._logFile)
+            if self._rotateMethod == 'time':
+                self._logHandler = logging.handlers.TimedRotatingFileHandler(self._logFile, \
+                                                                             when=self._timeRotateIntervalType, interval=self._timeRotateInterval, \
+                                                                             backupCount=self._backupCount, encoding=None, delay=False, utc=True)
+            elif self._rotateMethod == 'size':
+                self._logHandler = logging.handlers.RotatingFileHandler(self._logFile, \
+                                                                        mode=self._sizeRotateMode, maxBytes=self._sizeMaxBytes, \
+                                                                        backupCount=self._backupCount, encoding=None, delay=False)
+                
+            else:
+                self._logHandler = logging.handlers.WatchedFileHandler(self._logFile, \
+                                                                       mode=self._sizeRotateMode, \
+                                                                       encoding=None, delay=False)
+        elif self._typeLogger == 'stderrlogger':
+            self._logHandler = logging.handlers.StreamHandler(sys.stderr)
+        elif self._typeLogger == 'stdoutlogger':
+            self._logHandler = logging.handlers.StreamHandler(sys.stdout)
+        elif self._typeLogger == 'socketlogger':
+            self._logHandler = logging.handlers.SocketHandler(self._socketHost, self._socketPort)
+        elif self._typeLogger == 'nulllogger':
+            self._logHandler = logging.handlers.NullHandler()
+
+        if self._fields["style"] == CommonLogger.AuditFile or self._fields["style"] == CommonLogger.MetricsFile:
+            self._logFormatter = logging.Formatter(fmt='%(begtime)s,%(begmsecs)03d+00:00|%(endtime)s,%(endmsecs)03d+00:00|%(message)s', datefmt=CommonLogger.DateFmt)
+        else:
+            self._logFormatter = logging.Formatter(fmt='%(asctime)s,%(msecs)03d+00:00|%(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
+        self._logFormatter.converter = time.gmtime
+        self._logHandler.setFormatter(self._logFormatter)
+        self._logger.addHandler(self._logHandler)
+
+    def _resetStyleField(self):
+        styleFields = ["error", "debug", "audit", "metrics"]
+        if self._fields['style'] in styleFields:
+            self._fields['style'] = styleFields.index(self._fields['style'])
+
+    def __del__(self):
+        if self._monitorFlag == False:
+            return
+
+        self._monitorFlag = False
+
+        if self._monitorThread is not None and self._monitorThread.is_alive():
+            self._monitorThread.join()
+
+        self._monitorThread = None
+
+
+    def _defaultServerInfo(self):
+
+        # If not set or purposely set = None, then set default
+        if self._fields.get('server') is None:
+            try:
+                self._fields['server']          = socket.getfqdn()
+            except Exception as err:
+                try:
+                    self._fields['server']      = socket.gethostname()
+                except Exception as err:
+                    self._fields['server']      = ""
+
+        # If not set or purposely set = None, then set default
+        if self._fields.get('serverIPAddress') is None:
+            try:
+                self._fields['serverIPAddress'] = socket.gethostbyname(self._fields['server'])
+            except Exception as err:
+                self._fields['serverIPAddress'] = ""
+
+
+    def _monitorConfigFile(self):
+        while self._monitorFlag:
+            try:
+                fileTime = os.path.getmtime(self._configFile)
+                if fileTime > self._configFileModified:
+                    self._configFileModified = fileTime
+                    ReopenLogFile = False
+                    logFile = self._logFile
+                    with open(self._configFile) as fp:
+                        for line in fp:
+                            line = line.split('#',1)[0]    # remove comments
+                            if '=' in line:
+                                key, value = [x.strip() for x in line.split('=',1)]
+                                if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none'] and self._rotateMethod != value:
+                                    self._rotateMethod = value.lower()
+                                    ReopenLogFile = True
+                                elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
+                                    self._timeRotateIntervalType = value
+                                    ReopenLogFile = True
+                                elif key == 'timeRotateInterval' and int( value ) > 0:
+                                    self._timeRotateInterval = int( value )
+                                    ReopenLogFile = True
+                                elif key == 'sizeMaxBytes' and int( value ) >= 0:
+                                    self._sizeMaxBytes = int( value )
+                                    ReopenLogFile = True
+                                elif key == 'sizeRotateMode' and value in ['a']:
+                                    self._sizeRotateMode = value
+                                    ReopenLogFile = True
+                                elif key == 'backupCount' and int( value ) >= 0:
+                                    self._backupCount = int( value )
+                                    ReopenLogFile = True
+                                elif key == self._logKey + 'SocketHost' and self._socketHost != value:
+                                    self._socketHost = value
+                                    ReopenLogFile = True
+                                elif key == self._logKey + 'SocketPort' and self._socketPort > 0 and self._socketPort != int( value ):
+                                    self._socketPort = int( value )
+                                    ReopenLogFile = True
+                                elif key == self._logKey + 'LogLevel' and self._logLevelThreshold != self._intLogLevel( value.upper() ):
+                                    self._logLevelThreshold = self._intLogLevel(value.upper())
+                                elif key == self._logKey + 'LogType' and self._typeLogger != value and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
+                                    self._typeLogger = value.lower()
+                                    ReopenLogFile = True
+                                elif key == self._logKey + 'Style':
+                                    self._fields["style"] = value
+                                    self._resetStyleField()
+                                elif key == self._logKey and self._logFile != value:
+                                    logFile = value
+                                    ReopenLogFile = True
+                    if ReopenLogFile:
+                        with self._logLock:
+                            self._logger.removeHandler(self._logHandler)
+                            self._logFile = logFile
+                            self._createLogger()
+            except Exception as err:
+                pass
+
+            time.sleep(5)
+
+
+    def setFields(self, **kwargs):
+        """Set default values for log fields.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
+                    'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
+                    'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
+                    'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
+                    'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
+                    'errorDescription' ]:
+            if key in kwargs:
+                if kwargs[key] != None:
+                    self._fields[key] = kwargs[key]
+                elif key in self._fields:
+                    del self._fields[key]
+
+        self._defaultServerInfo()
+
+
+    def debug(self, message, **kwargs):
+        """Write a DEBUG level message to the log file.
+
+        Arguments:
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._log('DEBUG', message, errorCategory = 'DEBUG', **kwargs)
+
+    def info(self, message, **kwargs):
+        """Write an INFO level message to the log file.
+
+        Arguments:
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._log('INFO', message, errorCategory = 'INFO', **kwargs)
+
+    def warn(self, message, **kwargs):
+        """Write a WARN level message to the log file.
+
+        Arguments:
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._log('WARN', message, errorCategory = 'WARN', **kwargs)
+
+    def error(self, message, **kwargs):
+        """Write an ERROR level message to the log file.
+
+        Arguments:
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._log('ERROR', message, errorCategory = 'ERROR', **kwargs)
+
+    def fatal(self, message, **kwargs):
+        """Write a FATAL level message to the log file.
+
+        Arguments:
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        self._log('FATAL', message, errorCategory = 'FATAL', **kwargs)
+
+    def _log(self, logLevel, message, **kwargs):
+        """Write a message to the log file.
+
+        Arguments:
+        logLevel          -- value ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', ...) for the log record.
+        message           -- value for the last log record field.
+
+        Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
+        style                       -- the log file format (style) to use when writing log messages
+        requestID (dame)            -- optional default value for this log record field.
+        serviceInstanceID (am)      -- optional default value for this log record field.
+        threadID (am)               -- optional default value for this log record field.
+        serverName (am)             -- optional default value for this log record field.
+        serviceName (am)            -- optional default value for this log record field.
+        instanceUUID (am)           -- optional default value for this log record field.
+        severity (am)               -- optional default value for this log record field.
+        serverIPAddress (am)        -- optional default value for this log record field.
+        server (am)                 -- optional default value for this log record field.
+        IPAddress (am)              -- optional default value for this log record field.
+        className (am)              -- optional default value for this log record field.
+        timer (am)                  -- (ElapsedTime) optional default value for this log record field.
+        partnerName (ame)           -- optional default value for this log record field.
+        targetEntity (me)           -- optional default value for this log record field.
+        targetServiceName (me)      -- optional default value for this log record field.
+        statusCode (am)             -- optional default value for this log record field.
+        responseCode (am)           -- optional default value for this log record field.
+        responseDescription (am)    -- optional default value for this log record field.
+        processKey (am)             -- optional default value for this log record field.
+        targetVirtualEntity (m)     -- optional default value for this log record field.
+        customField1 (am)           -- optional default value for this log record field.
+        customField2 (am)           -- optional default value for this log record field.
+        customField3 (am)           -- optional default value for this log record field.
+        customField4 (am)           -- optional default value for this log record field.
+        errorCategory (e)           -- optional default value for this log record field.
+        errorCode (e)               -- optional default value for this log record field.
+        errorDescription (e)        -- optional default value for this log record field.
+
+        Note:  the pipe '|' character is not allowed in any log record field.
+        """
+
+        # timestamp will automatically be inserted
+        style              = int(self._getVal('style',             '', **kwargs))
+        requestID          = self._getVal('requestID',         '', **kwargs)
+        serviceInstanceID  = self._getVal('serviceInstanceID', '', **kwargs)
+        threadID           = self._getVal('threadID',          threading.currentThread().getName(), **kwargs)
+        serverName         = self._getVal('serverName',        '', **kwargs)
+        serviceName        = self._getVal('serviceName',       '', **kwargs)
+        instanceUUID       = self._getVal('instanceUUID',      '', **kwargs)
+        upperLogLevel      = self._noSep(logLevel.upper())
+        severity           = self._getVal('severity',          '', **kwargs)
+        serverIPAddress    = self._getVal('serverIPAddress',   '', **kwargs)
+        server             = self._getVal('server',            '', **kwargs)
+        IPAddress          = self._getVal('IPAddress',         '', **kwargs)
+        className          = self._getVal('className',         '', **kwargs)
+        timer              = self._getVal('timer',             '', **kwargs)
+        partnerName        = self._getVal('partnerName', '', **kwargs)
+        targetEntity       = self._getVal('targetEntity', '', **kwargs)
+        targetServiceName  = self._getVal('targetServiceName', '', **kwargs)
+        statusCode         = self._getVal('statusCode', '', **kwargs)
+        responseCode       = self._getVal('responseCode', '', **kwargs)
+        responseDescription = self._noSep(self._getVal('responseDescription', '', **kwargs))
+        processKey          = self._getVal('processKey', '', **kwargs)
+        targetVirtualEntity = self._getVal('targetVirtualEntity', '', **kwargs)
+        customField1        = self._getVal('customField1', '', **kwargs)
+        customField2        = self._getVal('customField2', '', **kwargs)
+        customField3        = self._getVal('customField3', '', **kwargs)
+        customField4        = self._getVal('customField4', '', **kwargs)
+        errorCategory       = self._getVal('errorCategory', '', **kwargs)
+        errorCode           = self._getVal('errorCode', '', **kwargs)
+        errorDescription    = self._noSep(self._getVal('errorDescription', '', **kwargs))
+
+        detailMessage     = self._noSep(message)
+        if bool(re.match(r" *$", detailMessage)):
+            return  # don't log empty messages
+
+        useLevel = self._intLogLevel(upperLogLevel)
+        if CommonLogger.verbose: print("logger STYLE=%s" % style)
+        if useLevel < self._logLevelThreshold:
+            if CommonLogger.verbose: print("skipping because of level")
+            pass
+        else:
+            with self._logLock:
+                if style == CommonLogger.ErrorFile:
+                    if CommonLogger.verbose: print("using CommonLogger.ErrorFile")
+                    self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+                                     %(requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName,
+                                       errorCategory, errorCode, errorDescription, detailMessage))
+                elif style == CommonLogger.DebugFile:
+                    if CommonLogger.verbose: print("using CommonLogger.DebugFile")
+                    self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+                                     %(requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel,
+                                       severity, serverIPAddress, server, IPAddress, className, timer, detailMessage))
+                elif style == CommonLogger.AuditFile:
+                    if CommonLogger.verbose: print("using CommonLogger.AuditFile")
+                    endAuditTime, endAuditMsec = self._getTime()
+                    if self._begTime is not None:
+                        d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
+                    else:
+                        d = { 'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
+                    self._begTime = None
+                    unused = ""
+                    self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+                                     %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
+                                       statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel,
+                                       severity, serverIPAddress, timer, server, IPAddress, className, unused,
+                                       processKey, customField1, customField2, customField3, customField4, detailMessage), extra=d)
+                elif style == CommonLogger.MetricsFile:
+                    if CommonLogger.verbose: print("using CommonLogger.MetricsFile")
+                    endMetricsTime, endMetricsMsec = self._getTime()
+                    if self._begTime is not None:
+                        d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
+                    else:
+                        d = { 'begtime': endMetricsTime, 'begmsecs': endMetricsMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
+                    self._begTime = None
+                    unused = ""
+                    self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
+                                     %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
+                                       targetEntity, targetServiceName, statusCode, responseCode, responseDescription,
+                                       instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server, IPAddress,
+                                       className, unused, processKey, targetVirtualEntity, customField1, customField2,
+                                       customField3, customField4, detailMessage), extra=d)
+                else:
+                    print("!!!!!!!!!!!!!!!! style not set: %s" % self._fields["style"])
+
+    def _getTime(self):
+        ct = time.time()
+        lt = time.localtime(ct)
+        return (time.strftime(CommonLogger.DateFmt, lt), (ct - int(ct)) * 1000)
+
+    def setStartRecordEvent(self):
+        """
+        Set the start time to be saved for both audit and metrics records
+        """
+        self._begTime, self._begMsec = self._getTime()
+
+    def _getVal(self, key, default, **kwargs):
+        val = self._fields.get(key)
+        if key in kwargs: val = kwargs[key]
+        if val is None: val = default
+        return self._noSep(val)
+
+    def _noSep(self, message):
+        if message is None:  return ''
+        return re.sub(r'[\|\n]', ' ', str(message))
+
+    def _intLogLevel(self, logLevel):
+        if   logLevel == 'FATAL':  useLevel = 50
+        elif logLevel == 'ERROR':  useLevel = 40
+        elif logLevel == 'WARN':   useLevel = 30
+        elif logLevel == 'INFO':   useLevel = 20
+        elif logLevel == 'DEBUG':  useLevel = 10
+        else:                      useLevel = 0
+        return useLevel
+
+    def _mkdir_p(self, filename):
+        """Create missing directories from a full filename path like mkdir -p"""
+
+        if filename is None:
+            return
+
+        folder=os.path.dirname(filename)
+
+        if folder == "":
+            return
+
+        if not os.path.exists(folder):
+            try:
+                os.makedirs(folder)
+            except OSError as err:
+                print("error number %d creating %s directory to hold %s logfile: %s" %(err.errno, err.filename, filename, err.strerror), file=sys.stderr)
+                sys.exit(2)
+            except Exception as err:
+                print("error creating %s directory to hold %s logfile: %s" %(folder, filename, str(err)), file=sys.stderr)
+                sys.exit(2)
+
+def __checkTime1(line):
+    format = r'[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9][+]00:00[|]'
+    format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[+]00:00[|]'
+    m = re.match(format, line)
+    if not m:
+        print("ERROR: time string did not match proper time format, %s" %line)
+        print("\t: format=%s" % format)
+        return 1
+    return 0
+
+def __checkTime2(line, different):
+    format = '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|][0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|]'
+    format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|][0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|]'
+    m = re.match(format, line)
+    if not m:
+        print("ERROR: time strings did not match proper time format, %s" %line)
+        print("\t: format=%s" % format)
+        return 1
+    second1 = int(m.group(1))
+    msec1 = int(m.group(2))
+    second2 = int(m.group(3))
+    msec2 = int(m.group(4))
+    if second1 > second2: second2 += 60
+    t1 = second1 * 1000 + msec1
+    t2 = second2 * 1000 + msec2
+    diff = t2 - t1
+    # print("t1=%d (%d,%d)  t2=%d (%d,%d), diff = %d" % (t1, second1, msec1, t2, second2, msec2, diff))
+    if different:
+        if diff < 500:
+            print("ERROR: times did not differ enough: %s" % line)
+            return 1
+    else:
+        if diff > 10:
+            print("ERROR: times were too far apart: %s" % line)
+            return 1
+    return 0
+
+def __checkLog(logfile, numLines, numFields):
+    lineCount = 0
+    errorCount = 0
+    with open(logfile, "r") as fp:
+        for line in fp:
+            # print("saw line %s" % line)
+            lineCount += 1
+            c = line.count('|')
+            if c != numFields:
+                print("ERROR: wrong number of fields. Expected %d, got %d: %s" % (numFields, c, line))
+                errorCount += 1
+            if re.search("should not appear", line):
+                print("ERROR: a line appeared that should not have appeared, %s" % line)
+                errorCount += 1
+            elif re.search("single time", line):
+                errorCount += __checkTime1(line)
+            elif re.search("time should be the same", line):
+                errorCount += __checkTime2(line, different=False)
+            elif re.search("time should be different", line):
+                errorCount += __checkTime2(line, different=True)
+            else:
+                print("ERROR: an unknown message appeared, %s" % line)
+                errorCount += 1
+
+    if lineCount != numLines:
+        print("ERROR: expected %d lines, but got %d lines" % (numLines, lineCount))
+        errorCount += 1
+    return errorCount
+
+if __name__ == "__main__":
+    import os, argparse
+    parser = argparse.ArgumentParser(description="test the CommonLogger functions")
+    parser.add_argument("-k", "--keeplogs", help="Keep the log files after finishing the tests", action="store_true")
+    parser.add_argument("-v", "--verbose", help="Print debugging messages", action="store_true")
+    args = parser.parse_args()
+        
+    spid = str(os.getpid())
+    if args.keeplogs:
+        spid = ""
+    logcfg = "/tmp/cl.log" + spid + ".cfg"
+    errorLog = "/tmp/cl.error" + spid + ".log"
+    metricsLog = "/tmp/cl.metrics" + spid + ".log"
+    auditLog = "/tmp/cl.audit" + spid + ".log"
+    debugLog = "/tmp/cl.debug" + spid + ".log"
+    if args.verbose: CommonLogger.verbose = True
+
+    import atexit
+    def cleanupTmps():
+        for f in [ logcfg, errorLog, metricsLog, auditLog, debugLog ]:
+            try:
+                os.remove(f)
+            except:
+                pass
+    if not args.keeplogs: 
+        atexit.register(cleanupTmps)
+
+    with open(logcfg, "w") as o:
+        o.write("error = " + errorLog + "\n" +
+                "errorLogLevel   = WARN\n" +
+                "metrics = " + metricsLog + "\n" +
+                "metricsLogLevel = INFO\n" +
+                "audit = " + auditLog + "\n" +
+                "auditLogLevel   = INFO\n" +
+                "debug = " + debugLog + "\n" +
+                "debugLogLevel   = DEBUG\n")
+
+    import uuid
+    instanceUUID = uuid.uuid1()
+    serviceName = "testharness"
+    errorLogger = CommonLogger(logcfg, "error", style=CommonLogger.ErrorFile, instanceUUID=instanceUUID, serviceName=serviceName)
+    debugLogger = CommonLogger(logcfg, "debug", style=CommonLogger.DebugFile, instanceUUID=instanceUUID, serviceName=serviceName)
+    auditLogger = CommonLogger(logcfg, "audit", style=CommonLogger.AuditFile, instanceUUID=instanceUUID, serviceName=serviceName)
+    metricsLogger = CommonLogger(logcfg, "metrics", style=CommonLogger.MetricsFile, instanceUUID=instanceUUID, serviceName=serviceName)
+
+    testsRun = 0
+    errorCount = 0
+    errorLogger.debug("error calling debug (should not appear)")
+    errorLogger.info("error calling info (should not appear)")
+    errorLogger.warn("error calling warn (single time)")
+    errorLogger.error("error calling error (single time)")
+    errorLogger.setStartRecordEvent()
+    time.sleep(1)
+    errorLogger.fatal("error calling fatal, after setStartRecordEvent and sleep (start should be ignored, single time)")
+    testsRun += 6
+    errorCount += __checkLog(errorLog, 3, 10)
+
+    auditLogger.debug("audit calling debug (should not appear)")
+    auditLogger.info("audit calling info (time should be the same)")
+    auditLogger.warn("audit calling warn (time should be the same)")
+    auditLogger.error("audit calling error (time should be the same)")
+    auditLogger.setStartRecordEvent()
+    time.sleep(1)
+    auditLogger.fatal("audit calling fatal, after setStartRecordEvent and sleep, time should be different)")
+    testsRun += 6
+    errorCount += __checkLog(auditLog, 4, 25)
+
+    debugLogger.debug("debug calling debug (single time)")
+    debugLogger.info("debug calling info (single time)")
+    debugLogger.warn("debug calling warn (single time)")
+    debugLogger.setStartRecordEvent()
+    time.sleep(1)
+    debugLogger.error("debug calling error, after SetStartRecordEvent and sleep (start should be ignored, single time)")
+    debugLogger.fatal("debug calling fatal (single time)")
+    errorCount += __checkLog(debugLog, 5, 13)
+    testsRun += 6
+
+    metricsLogger.debug("metrics calling debug (should not appear)")
+    metricsLogger.info("metrics calling info (time should be the same)")
+    metricsLogger.warn("metrics calling warn (time should be the same)")
+    metricsLogger.setStartRecordEvent()
+    time.sleep(1)
+    metricsLogger.error("metrics calling error, after SetStartRecordEvent and sleep, time should be different")
+    metricsLogger.fatal("metrics calling fatal (time should be the same)")
+    testsRun += 6
+    errorCount += __checkLog(metricsLog, 4, 28)
+
+    print("%d tests run, %d errors found" % (testsRun, errorCount))
diff --git a/osdf/logging/onap_common_v1/CommonLogger_test.config b/osdf/logging/onap_common_v1/CommonLogger_test.config
new file mode 100755 (executable)
index 0000000..584fb5e
--- /dev/null
@@ -0,0 +1,58 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+# You may change this file while your program is running and CommonLogger will automatically reconfigure accordingly.
+# Changing these parameters may leave old log files lying around.
+
+
+#--- Parameters that apply to all logs
+#
+# rotateMethod:  time, size, stdout, stderr, none
+#... Note:  the following two parameters apply only when rotateMethod=time
+# timeRotateIntervalType:  S, M, H, D, W0 - W6, or midnight  (seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC)
+# timeRotateInterval:  >= 1  (1 means every timeRotateIntervalType, 2 every other, 3 every third, etc.)
+#... Note:  the following parameter applies only when rotateMethod=size
+# sizeMaxBytes:  >= 0  (0 means no limit, else maximum filesize in Bytes)
+# backupCount:  >= 0  (Number of rotated backup files to retain.  If rotateMethod=time, 0 retains *all* backups.  If rotateMethod=size, 0 retains *no* backups.)
+#
+rotateMethod           = time
+timeRotateIntervalType = midnight
+timeRotateInterval     = 1
+sizeMaxBytes           = 0
+backupCount            = 6
+
+
+#--- Parameters that define log filenames and their initial LogLevel threshold
+#... Note:  CommonLogger will exit if your process does not have permission to write to the file.
+#
+
+error           = /opt/logs/oof/error.log
+errorLogLevel   = WARN
+errorStyle      = error
+
+metrics         = /opt/logs/oof/metrics.log
+metricsLogLevel = INFO
+metricsStyle    = metrics
+
+audit           = /opt/logs/oof/audit.log
+auditLogLevel   = INFO
+auditStyle      = audit
+
+debug           = /opt/logs/oof/debug.log
+debugLogLevel   = DEBUG
+debugStyle      = debug
diff --git a/osdf/logging/onap_common_v1/CommonLogger_testing.py b/osdf/logging/onap_common_v1/CommonLogger_testing.py
new file mode 100755 (executable)
index 0000000..43e0ec3
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+"""
+Test the ONAP Common Logging library in Python.
+CommonLogger_test.py
+"""
+
+
+from __future__ import print_function   # for the example code below parsing command line options
+import os, sys, getopt                  # for the example code below parsing command line options
+
+from osdf.logging.onap_common_v1.CommonLogger import CommonLogger                     # all that is needed to import the CommonLogger library
+
+import uuid                             # to create UUIDs for our log records
+import time                             # to create elapsed time for our log records
+
+
+#----- A client might want to allow specifying the configFile as a command line option
+usage="usage: %s [ -c <configFile> ]" % ( os.path.basename(__file__) )
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "c:")
+except getopt.GetoptError:
+    print(usage, file=sys.stderr)
+    sys.exit(2)
+
+configFile = "CommonLogger_test.config"
+for opt, arg in opts:
+    if opt == "-c":
+        configFile = arg
+    else:
+        print(usage, file=sys.stderr)
+        sys.exit(2)
+
+
+#----- Instantiate the loggers
+
+# The client's top-level program (e.g., vPRO.py) can create a unique identifier UUID to differentiate between multiple instances of itself.
+instanceUUID = uuid.uuid1()
+
+# The client should identify its ONAP component -- and if applicable -- its ONAP sub-component
+serviceName = "DCAE/vPRO"
+
+# Instantiate using a configuration file with a key specifying the log file name and set fields' default values
+errorLog =   CommonLogger.CommonLogger(configFile, "error",   instanceUUID=instanceUUID, serviceName=serviceName)
+metricsLog = CommonLogger.CommonLogger(configFile, "metrics", instanceUUID=instanceUUID, serviceName=serviceName)
+auditLog =   CommonLogger.CommonLogger(configFile, "audit",   instanceUUID=instanceUUID, serviceName=serviceName)
+debugLog =   CommonLogger.CommonLogger(configFile, "debug",   instanceUUID=instanceUUID, serviceName=serviceName)
+
+
+#----- use the loggers
+
+# both metrics and audit logs can have an event starting time. This only affects the next log message.
+metricsLog.setStartRecordEvent()
+auditLog.setStartRecordEvent()
+
+# Simple log messages
+debugLog.debug("a DEBUG message for the debug log")
+metricsLog.info("an INFO message for the metrics log")
+auditLog.info("an INFO message for the audit log")
+errorLog.warn("a WARN message for the error log")
+errorLog.error("an ERROR message for the error log")
+errorLog.fatal("a FATAL message for the error log")
+
+
+# Can override any of the other fields when writing each log record
+debugLog.debug("demonstrating overriding all fields with atypical values", requestID="2", serviceInstanceID="3", threadID="4", serverName="5", serviceName="6", instanceUUID="7", severity="9", serverIPAddress="10", server="11", IPAddress="12", className="13", timer="14")
+
+
+# The is an example of an interaction between two ONAP components:
+
+# vPRO generates Closed Loop RESTful API requests to App-C, knowing this information:
+requestClient = "netman@localdcae.att.com:~/vPRO_trinity/vPRO.py:905"  # uniquely identifies the requester
+requestTime = "2015-08-20 20:57:14.463426"                             # unique ID of the request within the requester's scope
+request = "Restart"
+
+# Form the value for Common Logging's requestID field:
+requestID = requestClient + "+" + requestTime  # vPRO will use this as the unique requestID
+# requestID = uuid.uuid1()  # other services might generate a UUID as their requestID
+
+# Form the value for Common Logging's serviceName field when an interaction between two ONAP components:
+ourONAP = serviceName
+peerONAP = "App-C"
+operation = request
+interaction = ourONAP + ":" + peerONAP + "." + operation
+
+# Let's calculate and report elapsed times
+start = time.time()
+
+# Log the request
+auditLog.info("Requesting %s to %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction)
+
+# Wait for first response
+time.sleep(1)  # simulate processing the action, e.g., waiting for response from App-C
+
+# Form the value for Common Logging's serviceName field when an interaction between two ONAP components:
+operation = 'PENDING'
+interaction = peerONAP + ":" + ourONAP + "." + operation
+
+# Log the response with elapsed time
+ms = int(round(1000 * (time.time() - start)))  # Calculate elapsed time in ms
+auditLog.info("%s acknowledged receiving request for %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction, timer=ms)
+
+# Wait for next response
+time.sleep(1)  # simulate processing the action, e.g., waiting for response from App-C
+
+# Form the value for Common Logging's serviceName field when an interaction between two ONAP components:
+operation = 'SUCCESS'
+interaction = peerONAP + ":" + ourONAP + "." + operation
+
+# Log the response with elapsed time
+ms = int(round(1000 * (time.time() - start)))  # Calculate elapsed time in ms
+auditLog.info("%s finished %s" %(peerONAP, operation), requestID=requestID, serviceName=interaction, timer=ms)
+
+
+# Can change the fields' default values for a logger after instantiation if desired
+debugLog.setFields(serviceName="DCAE", threadID='thread-2')
+
+# Then subsequent logging will have the new default field values
+debugLog.info("Something happened")
+debugLog.warn("Something happened again")
+
+
+# Unset (set=None) a field so the Common Logger will use the default value
+debugLog.info("threadID should be default", threadID=None)
+debugLog.setFields(threadID=None)
+debugLog.info("threadID should be default")
diff --git a/osdf/logging/onap_common_v1/README.md b/osdf/logging/onap_common_v1/README.md
new file mode 100755 (executable)
index 0000000..596cd7f
--- /dev/null
@@ -0,0 +1,214 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+# Common Logging Wrapper for Python
+
+* CommonLogger.py is the module (library) to import
+* CommonLogger_test.config is an example configuration file used by CommonLogger_test.py
+* CommonLogger_test.py is an example of how to import and use the CommonLogger module
+
+## Configuration File
+
+Configure common logging for a python application in a configuration file.
+In the file, put key = value assignments
+
+* defining the filename for each log file you will create, such as
+'error=/path/error.log', 'metrics=/path/metrics.log', 'audit=/path/audit.log',
+and 'debug=/path/debug.log'.
+The name used (shown here as 'error', 'metrics', etc.) is chosen in the program, allowing a single configuration file to be
+used by numerous different programs.
+(It will be referred to below as &lt;logKey&gt;.)
+* defining the style of the log messages to be produced,
+using &lt;logKey&gt; suffixed with 'Style', as in 'errorStyle=', and one of the
+words 'error', 'metrics', 'audit' and 'debug'.
+* defining the minimum level of log messages to be retained in a log file,
+using &lt;logKey&gt; suffixed with 'LogLevel', as in 'errorLogLevel=WARN'.
+The levels are DEBUG, INFO, WARN, ERROR, and FATAL.
+So specifying WARN will retain only WARN, ERROR, and FATAL level
+log messages, while specifying DEBUG will retain all levels of log messages:
+DEBUG, INFO, WARN, ERROR, and FATAL.
+
+Comments may be included on any line following a '#' character.
+
+Common logging monitors the configuration file so if the file is edited
+and any its values change, then common logging will implement the changes
+in the running application.
+This enables operations to change log levels or even log filenames without
+interrupting the running application.
+
+By default, log files are rotated daily at midnight UTC, retaining 6 backup versions by default.
+
+Other strategies can be specified within the configuration file using the keywords:
+
+* rotateMethod = one of 'time', 'size', and 'none' (case insensitive)
+
+If rotateMethod is 'time', the following keywords apply:
+* backupCount = Number of rotated backup files to retain, >= 0. 0 retains *all* backups.
+* timeRotateIntervalType = one of 'S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight'
+(seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC)
+* timeRotateInterval = number of seconds/minutes/hours/days between rotations. (Ignored for W#.)
+
+If rotateMethod is 'size', the following keywords apply:
+* backupCount = Number of rotated backup files to retain, >= 0. 0 retains *no* backups.
+* sizeMaxBytes = maximum number of bytes allowed in the file before rotation
+* sizeRotateMode = for now, this defaults to 'a' and may only be specified as 'a'.
+It is passed to the underlying Python Logging methods.
+
+
+Besides logging to a file, it is also possible to send log messages elsewhere,
+using &lt;logKey&gt; suffixed with 'LogType'.
+You can set &lt;logKey&gt;LogType to any of 'filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger' orlogger 'null' (case insensitive).
+
+* 'filelogger' is the default specifying logging to a file.
+* 'stdoutlogger' and 'stderrlogger' send the output to the corresponding output streams.
+* 'socketlogger' will send the output to the corresponding socket host.
+* 'nulllogger' turns off logging.
+
+If &lt;logKey&gt;LogType is 'socket', the following keywords apply:
+* &lt;logKey&gt;SocketHost = FQDN or IP address for a host to sent the logs to
+* &lt;logKey&gt;SocketPort = the port (> 0) to open on that host
+
+This is an example configuration file:
+
+    error           = /var/log/DCAE/vPRO/error.log
+    errorLogLevel   = WARN
+    errorStyle      = error
+
+    metrics         = /var/log/DCAE/vPRO/metrics.log
+    metricsLogLevel = INFO
+    metricsStyle      = metrics
+
+    audit           = /var/log/DCAE/vPRO/audit.log
+    auditLogLevel   = INFO
+    auditStyle      = audit
+
+    debug           = /var/log/DCAE/vPRO/debug.log
+    debugLogLevel   = DEBUG
+    debugStyle      = debug
+
+## Coding Python Applications to Produce ONAP Common Logging
+
+A python application uses common logging by importing the CommonLogger
+module, instantiating a CommonLogger object for each log file, and then
+invoking each object's debug, info, warn, error, or fatal methods to log
+messages to the file. There are four styles of logging:
+error/info logs, debug logs, audit logs, and metrics logs.
+The difference between the types of logs is in the list of fields that
+are printed out.
+
+### Importing the CommonLogger Module
+
+Importing the CommonLogger module is typical:
+
+    sys.path.append("/opt/app/dcae-commonlogging/python")
+    import CommonLogger
+
+### Creating a CommonLogger object:
+
+When creating a CommonLogger object, three arguments are required:
+
+1. The configuration filename.
+2. The keyword name in the configuration file that
+defines the log filename and parameters controlling rotation of the logfiles.
+(This is the &lt;logKey&gt; referred to above.)
+3. The keyword arguments for style and to set default values for the log record fields.
+
+The style of the log (one of CommonLoger.DebugFile, CommonLogger.AuditFile,
+CommonLogger.MetricsFile and CommonLogger.ErrorFile), must be specified either
+in the configuration file (e.g., errorStyle=error or metricsStyle=metrics) or
+using a style= keyword and one of the values: CommonLoger.DebugFile,
+CommonLogger.AuditFile, CommonLogger.MetricsFile and CommonLogger.ErrorFile.
+
+Keyword arguments for log record fields are as follows.
+The annotation indicates whether the field is included in
+(d) debug logs, (a) audit logs, (m) metrics logs, and (e) error logs.
+
+* requestID (dame)
+* serviceInstanceID (am)
+* threadID (am)
+* serverName (am)
+* serviceName (am)
+* instanceUUID (am)
+* severity (am)
+* serverIPAddress (am)
+* server (am)
+* IPAddress (am)
+* className (am)
+* timer (am)
+* partnerName (ame)
+* targetEntity (me)
+* targetServiceName (me)
+* statusCode (am)
+* responseCode (am)
+* responseDescription (am)
+* processKey (am)
+* targetVirtualEntity (m)
+* customField1 (am)
+* customField2 (am)
+* customField3 (am)
+* customField4 (am)
+* errorCategory (e)
+* errorCode (e)
+* errorDescription (e)
+
+Sample code:
+
+    """ The style can be specified here or in the config file using errorStyle. """
+    errorLog = CommonLogger.CommonLogger("my.config", "error", style=CommonLogger.ErrorFile, serviceName="DCAE/vPRO")
+    infoLog = CommonLogger.CommonLogger("my.config", "info", serviceName="DCAE/vPRO")
+
+### Setting default values for fields:
+
+The object's setFields method allows keyword arguments changing default values for the log record fields.
+
+    errorLog.setFields(serviceName="DCAE/vPRO", threadID="thread-2")
+
+### Calling Methods
+
+The object's debug(), info(), warn(), error(), and fatal() methods require a detailMessage argument
+(which can be a zero-length string) and allow the keyword arguments for setting log record field
+values for just that one message.
+Any newlines or '|' characters in the message will be changed to a single space.
+
+    infoLog.info("Something benign happened.")
+    errorLog.fatal("Something very bad happened.", threadID="thread-4")
+
+### Output
+
+Note that no field may contain the '|' (pipe) field separation character, as that
+character is used as the separator between fields.
+Here is a possible example of a produced log record:
+
+    2015-10-12T15:56:43,182+00:00|netman@localdcae.att.com:~/vPRO_trinity/vPRO.py:905+2015-08-20 20:57:14.463426||||DCAE/vPRO:App-C.Restart|d4d5fc66-70f9-11e5-b0b1-005056866a82|INFO||135.16.76.33|mtvpro01dev1.dev.att.com|||1001|Finished Restart
+    2016-12-09T23:06:02,314+00:00||MainThread|DCAE/vPRO|||||||a FATAL message for the error log
+
+### Example Code
+
+The main within CommonLogger.py contains a regression test of the CommonLogger methods.
+
+CommonLogger_test.py contains a complete demonstration of a python application
+using the python CommonLogging wrapper module, including creating UUIDs,
+setting default log field values, and timing operations.
+
+## Upgrading from Previous Versions of CommonLogger
+
+The current version of CommonLogger is 99% compatible with earlier versions of CommonLogger.
+The key change, due to update ONAP logging requirements, is the choice to use different lists
+of fields in different types of log files.
+This required adding a mandatory "style" to be given, which we chose to do using either a
+new keyword in the configuration file, or using a new parameter keyword when creating the logger.
diff --git a/osdf/logging/onap_common_v1/__init__.py b/osdf/logging/onap_common_v1/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/osdf/logging/onap_common_v1/makefile b/osdf/logging/onap_common_v1/makefile
new file mode 100755 (executable)
index 0000000..498127e
--- /dev/null
@@ -0,0 +1,40 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+test:
+       rm -f /tmp/cl.*.log
+       python CommonLogger.py
+       rm -f /tmp/cl.*.log
+       python3 CommonLogger.py -k -v
+       # python CommonLogger_test.py
+       # python3 CommonLogger_test.py
+
+# STAGEDIR is overridden in ../makefile
+STAGEDIR=/tmp
+
+build: CommonLogger.html
+       mkdir -p $(STAGEDIR)/python
+       cp -p *.py *.config *.md CommonLogger.html $(STAGEDIR)/python
+       chmod a+x $(STAGEDIR)/python/*.py
+
+CommonLogger.html: CommonLogger.py
+       pydoc -w ./CommonLogger.py
+
+clean:
+       rm -rf __pycache__ *.pyc CommonLogger.html
+       rm -rf *~ 
index 9a6ff4e..a54d426 100755 (executable)
@@ -1,13 +1,29 @@
-import logging
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
 import traceback
 import uuid
 
-import logging
-from logging.handlers import RotatingFileHandler
+from .onap_common_v1.CommonLogger import CommonLogger
 from osdf.utils.programming_utils import MetaSingleton
 
 
-def log_handlers_pre_onap(config_file="config/pre_onap_logging_common_v1.config",
+def log_handlers_pre_onap(config_file="config/onap_logging_common_v1.config",
                           service_name="OOF_OSDF"):
     """
     Convenience handlers for logging to different log files
@@ -24,9 +40,8 @@ def log_handlers_pre_onap(config_file="config/pre_onap_logging_common_v1.config"
     X["metrics"].info("an INFO message for the metrics log")
     X["debug"].debug("a DEBUG message for the debug log")
     """
-    # Keeping main_params as a place-holder for ONAP related logging needs
-    # main_params = dict(instanceUUID=uuid.uuid1(), serviceName=service_name, configFile=config_file)
-    return dict((x, logging.getLogger(x))   # keep **main_params as a placeholder for ONAP fields
+    main_params = dict(instanceUUID=uuid.uuid1(), serviceName=service_name, configFile=config_file)
+    return dict((x, CommonLogger(logKey=x, **main_params))
                 for x in ["error", "metrics", "audit", "debug"])
 
 
@@ -215,6 +230,7 @@ class OOF_OSDFLogMessageFormatter(object):
 MH = OOF_OSDFLogMessageFormatter
 error_log, metrics_log, audit_log, debug_log = OOF_OSDFLogMessageHelper().get_handlers()
 
+
 def warn_audit_error(msg):
     """Log the message to error_log.warn and audit_log.warn"""
     log_message_multi(msg, audit_log.warn, error_log.warn)
index 09a89b0..698d922 100755 (executable)
@@ -97,6 +97,7 @@ def handle_data_error(e):
 @app.route("/api/oof/v1/healthcheck", methods=["GET"])
 def do_osdf_health_check():
     """Simple health check"""
+    audit_log.info("A health check request is processed!")
     return "OK"
 
 
diff --git a/ssl_certs/oof.crt b/ssl_certs/oof.crt
new file mode 100644 (file)
index 0000000..dc61a43
--- /dev/null
@@ -0,0 +1,59 @@
+Bag Attributes
+    localKeyID: F5 64 7B F8 32 67 FD CE 81 5E 0D 13 36 B7 67 35 47 33 B8 9B 
+    friendlyName: oof@oof.onap.org
+subject=/C=US/O=ONAP/OU=oof@oof.onap.org/OU=OSAAF/CN=oof.api.simpledemo.onap.org
+issuer=/C=US/O=ONAP/OU=OSAAF/CN=intermediateCA_1
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEN
+MAsGA1UECgwET05BUDEOMAwGA1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVk
+aWF0ZUNBXzEwHhcNMTgwNDI1MTIxMzAxWhcNMTkwNDIwMTIxMzAxWjBtMQswCQYD
+VQQGEwJVUzENMAsGA1UECgwET05BUDEZMBcGA1UECwwQb29mQG9vZi5vbmFwLm9y
+ZzEOMAwGA1UECwwFT1NBQUYxJDAiBgNVBAMMG29vZi5hcGkuc2ltcGxlZGVtby5v
+bmFwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGpQUtgLXG3
+dVikd/QC2Q24wzeTOeZzbx3PnidNYZT5K0sJ/TdnZF6O/4+9gXQ6AQS2Q8wfQ009
+MQAA5vhUaq5yZ2K+XAtEFGln1TxTFpGu3WDOwQ800Vw18Dk8WidrkzDJv489Bn1f
+SSaPC0IaRB0K1d8BD63ZHgsuEY8lt31DX2wFWJcfN9mxNDzuLTZoLxtxKsedoZKH
+rsOOILwXOhwuunfx40i6RQN/pFX6C2i8dtOA5OwUm9Q1RrZ2Tv1Uf4IURriH6bfZ
+5n50yxTuL22TMYXsF/ohrdgwacuC0aV9ZSGhIZUJPyHVg7+QTBioHmoUJInVKuIx
+kkC4lENbLYUCAwEAAaOB+jCB9zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG
+wDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp
+ZmljYXRlMB0GA1UdDgQWBBQwbU5oHU2iYHCoVz4hFCvBW59cdTBUBgNVHSMETTBL
+gBQd5lldG54KOKRipsGF8/PP1vGX6qEwpC4wLDEOMAwGA1UECwwFT1NBQUYxDTAL
+BgNVBAoMBE9OQVAxCzAJBgNVBAYTAlVTggEBMA4GA1UdDwEB/wQEAwIF4DAdBgNV
+HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBADEa
+0VuxoFIygeQTqlizpHNwfApPmlAVSKDTWuEu4rhJs8GT61EuWZQPygXEUHCYmGvJ
+GMwEGGIDGiQqxMqlqng46gksNJbi1ktXr6Du18qW7gziUd84ve8KcecjZru1Sk1e
+UJ/6WEQVE17CHKcnzQZsMDakgP+61VgKbk5NlkeF/Qh4L6/3jY7g+xoXqaId5RT9
+BetmH/cMsj33lxQTs0fcXTbAQd6BX5ug854OJ1mU4ngJnNBdmn9Ow1bB71ohf5Xv
+OEYX8+khjgjlmM0u1hBRL4qViv3y2Gzhpm1M8cETMDj4g0zIJytzIYMxO8XvDPCF
+YmVZHXJDLsCogSOmmh0=
+-----END CERTIFICATE-----
+Bag Attributes: <No Attributes>
+subject=/C=US/O=ONAP/OU=OSAAF/CN=intermediateCA_1
+issuer=/OU=OSAAF/O=ONAP/C=US
+-----BEGIN CERTIFICATE-----
+MIIEVDCCAjygAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMQ4wDAYDVQQLDAVPU0FB
+RjENMAsGA1UECgwET05BUDELMAkGA1UEBhMCVVMwHhcNMTgwNDA1MTQxNTQwWhcN
+MTgwNjA0MTQxNTQwWjBHMQswCQYDVQQGEwJVUzENMAsGA1UECgwET05BUDEOMAwG
+A1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVkaWF0ZUNBXzEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY3YPA/YQdz4kaZQzdRzWNjmn33WYAWZ8+
+EIz3PhkEzk7M1q9N7Icx2LvozMj4VH0yGz/HYlliHhw26ZRsjYMSR8zATsXl4oW9
+w9BrjuyvM3w8Ptxe8WbUFF9LJDGyXPeVvcXVo0iyh3QYPWC/AWmomN19MvBFN5vH
+AvEG/7qtonViNfISW9Gr9LpXB0foCmUDBu/lV+SwRGajoCPqdZhZ6/L6/yqDvha2
+wsML/UZXlGhXAedt/xOKmT/dSXx/I0vWBVp6Tq4zu87yCvd+I6Tpa5HjttA2I5EV
+zdHX+JYBPBBcVCyO9YQOYjJuoVDE4D5etY6dEipKG/KZF/rqAoqZAgMBAAGjZjBk
+MB0GA1UdDgQWBBQd5lldG54KOKRipsGF8/PP1vGX6jAfBgNVHSMEGDAWgBRTVTPy
+S+vQUbHBeJrBKDF77+rtSTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE
+AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAmgeiitBDi/YEqFh2Cqp0VIEqw8hiuV87
+rADQWMK4hv5WXl3KJTjFAnWsYFUKrm6s1jNH16FyGExUQgwggob0Vt+MHiUs36jU
+kyret/uE5qrjz+/J+i2XG6s1oKcDRVD/jU4qBygZWFBMuwl7sz8IEvaYXGM43s96
+Du3UF9E+V3aMppqkGWz6MnrTmANnWAlDAMeifcoexjrpxiKbp8f49HX1UzwFoeEg
+RnVwNqgDWT66yGV6mbNl6FpE/U81RpCRY1ZJDeVTxbqIaG/UPV4hpQ+BEVBDF+cb
+rGsvsNYYpWx5srIQ7WtGKIlaDFbfWPwnHDHegzr8ypAS3KNWULE+QXCbHWtB+b0Y
+WhP/2F6Jjb+ByvJqQoE+nHEYBeUOZUUZC4IuQFNJ5Wy5P0CNXdheiWhdrBmG02Gy
+KMi0FJx6BEoWM2xcdl6bn5j9mhF4TX7zgepNWlgTra4Z8Oz8iqbQk33/s2OKM4ic
+6ZezUYhNp+MuUt4Se+ufNcGV65jnUKeROtWzNLwP+xwglEFlG8aNiAORthd7QJuT
+Ey2cX7H7f38ENQ5YCriUk1nVLO9F66l/rNRzYZgQzRI3IvDW8vyM2TLW2mcZNsaf
+qjFMcCDweV2FRb8eTbmWzzB2/xTVpGzVJqzwgE+U7UtJx5CZS3wPkvXuEgvcg1tY
+m1r4NGYFvLM=
+-----END CERTIFICATE-----
diff --git a/ssl_certs/oof.crt.pem b/ssl_certs/oof.crt.pem
new file mode 100644 (file)
index 0000000..4c6eb91
--- /dev/null
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----\r
+MIIEKjCCAxKgAwIBAgIBHjANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEN\r
+MAsGA1UECgwET05BUDEOMAwGA1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVk\r
+aWF0ZUNBXzEwHhcNMTgwNDI1MTIxMzAxWhcNMTkwNDIwMTIxMzAxWjBtMQswCQYD\r
+VQQGEwJVUzENMAsGA1UECgwET05BUDEZMBcGA1UECwwQb29mQG9vZi5vbmFwLm9y\r
+ZzEOMAwGA1UECwwFT1NBQUYxJDAiBgNVBAMMG29vZi5hcGkuc2ltcGxlZGVtby5v\r
+bmFwLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANGpQUtgLXG3\r
+dVikd/QC2Q24wzeTOeZzbx3PnidNYZT5K0sJ/TdnZF6O/4+9gXQ6AQS2Q8wfQ009\r
+MQAA5vhUaq5yZ2K+XAtEFGln1TxTFpGu3WDOwQ800Vw18Dk8WidrkzDJv489Bn1f\r
+SSaPC0IaRB0K1d8BD63ZHgsuEY8lt31DX2wFWJcfN9mxNDzuLTZoLxtxKsedoZKH\r
+rsOOILwXOhwuunfx40i6RQN/pFX6C2i8dtOA5OwUm9Q1RrZ2Tv1Uf4IURriH6bfZ\r
+5n50yxTuL22TMYXsF/ohrdgwacuC0aV9ZSGhIZUJPyHVg7+QTBioHmoUJInVKuIx\r
+kkC4lENbLYUCAwEAAaOB+jCB9zAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIG\r
+wDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRp\r
+ZmljYXRlMB0GA1UdDgQWBBQwbU5oHU2iYHCoVz4hFCvBW59cdTBUBgNVHSMETTBL\r
+gBQd5lldG54KOKRipsGF8/PP1vGX6qEwpC4wLDEOMAwGA1UECwwFT1NBQUYxDTAL\r
+BgNVBAoMBE9OQVAxCzAJBgNVBAYTAlVTggEBMA4GA1UdDwEB/wQEAwIF4DAdBgNV\r
+HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBADEa\r
+0VuxoFIygeQTqlizpHNwfApPmlAVSKDTWuEu4rhJs8GT61EuWZQPygXEUHCYmGvJ\r
+GMwEGGIDGiQqxMqlqng46gksNJbi1ktXr6Du18qW7gziUd84ve8KcecjZru1Sk1e\r
+UJ/6WEQVE17CHKcnzQZsMDakgP+61VgKbk5NlkeF/Qh4L6/3jY7g+xoXqaId5RT9\r
+BetmH/cMsj33lxQTs0fcXTbAQd6BX5ug854OJ1mU4ngJnNBdmn9Ow1bB71ohf5Xv\r
+OEYX8+khjgjlmM0u1hBRL4qViv3y2Gzhpm1M8cETMDj4g0zIJytzIYMxO8XvDPCF\r
+YmVZHXJDLsCogSOmmh0=\r
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/ssl_certs/oof_new.key b/ssl_certs/oof_new.key
new file mode 100644 (file)
index 0000000..b3208c1
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0alBS2Atcbd1WKR39ALZDbjDN5M55nNvHc+eJ01hlPkrSwn9
+N2dkXo7/j72BdDoBBLZDzB9DTT0xAADm+FRqrnJnYr5cC0QUaWfVPFMWka7dYM7B
+DzTRXDXwOTxaJ2uTMMm/jz0GfV9JJo8LQhpEHQrV3wEPrdkeCy4RjyW3fUNfbAVY
+lx832bE0PO4tNmgvG3Eqx52hkoeuw44gvBc6HC66d/HjSLpFA3+kVfoLaLx204Dk
+7BSb1DVGtnZO/VR/ghRGuIfpt9nmfnTLFO4vbZMxhewX+iGt2DBpy4LRpX1lIaEh
+lQk/IdWDv5BMGKgeahQkidUq4jGSQLiUQ1sthQIDAQABAoIBAHeHah1B6MajE/iE
+U4q+sOYcxtcBTYovl1LEkeLQP+jBoUf3mvAiNtud5N8a6BnOE9SO4NoXnLQFRdE9
+snAzGFr6CC0IX8tgdc6eDriEmiJWMgnF9dTohM9wRNMssC03LEQtUNOls/R4BWlB
+NebquJhiHAo2Pa0cUf+HtSUKGLEFVqyGyf/psqw+y38VP5ZVv5BvlPGRsSyExbwD
+uZ7QNC5szL7k1kqsiQ0nRxHBZxTI9gBQr2LKM8TY4TAmFr2JIoFDr9BDZ5GANzGR
+aglyQWERRuNhGDkS9Okn/vfxjhUcuaNciULUyIMt0RT3IlgmgWyWqk75xueaCiMr
+kpFWRWECgYEA72WwP+rqv6gM88kD+zBKcyianW6TYSN6TDBpzX4StcPr32KYqvXW
+CXgUUjfZQduyNrfxxI7C/6fGWT6oj3G7I3dI+GXMQ6TYWUIos0uhL4SBPZa04hKf
+Y3P6PBFGOqv301/mwS5MI2sMOBrpJH/hig0ExXrzM2EAQi7V6adji5kCgYEA4DOg
+NTuLaB0FinHzPCySiujjcAWBsvjhpF+C3g3RMOFC0EKCy3snPnxyLYQENcIueE9r
+9y68pnpqNqFWOJqLINc727cU2+becFfpinGQEnZuC/48FbiDDR2uTv/vd4OT8+ng
+tuNGXbBz/XP9nvjS5t06MDOrOrseBSpo3ZfmBM0CgYEAxQCOgJrl4R/+wKL75rp/
+mbKhQcqb94UFgCsa9iK4bOG0ehid/5ncL+CkAGC7JWoQhtzqVNESgOXk4M4iUiDK
+Wk4wO1EyPbwq2ZELAzjKhNrqq+8YHS4sAeCP3NxuSZv4jfZOY0yhFUhjPsxObV3b
+EQrTkVszRWWem9gE6ol37okCgYEAhEeRb7b5Em2FFmES/N7je1fa0P4+vuS+5OeB
+ZBhM44UUkaGcYAgCaIiuKRKqFTnDhzJ85fNKVQMG5cKdB3qPOcojxAeqI/B8L1Z/
+MTK9qVb8qNDQjJQ3piZr8KpqlF4qjg/giKdhned9F/42lnQCoznFmijyDw3VsYCL
+LKrxiMUCgYAvq51mzXuGRGEJp8QmVBJGfSwIlqB9F5zdkVfWADP6X99MSH0PGpvU
+SJOYO9gQJA31v3AECLUXYjYFlEX4PcAhMCwVONm2AAok0EXIc1UgJrpNkdRIjhJW
+81NkKznllRF7LownV1zoOl9CcIn8u9XoRd1OjRTzU8QTZ1QfLkexoQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test/functest/simulators/simulated-config/onap_logging_common_v1.config b/test/functest/simulators/simulated-config/onap_logging_common_v1.config
new file mode 100755 (executable)
index 0000000..56f58d3
--- /dev/null
@@ -0,0 +1,58 @@
+# -------------------------------------------------------------------------
+#   Copyright (c) 2015-2017 AT&T Intellectual Property
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# -------------------------------------------------------------------------
+#
+
+# You may change this file while your program is running and CommonLogger will automatically reconfigure accordingly.
+# Changing these parameters may leave old log files lying around.
+
+
+#--- Parameters that apply to all logs
+#
+# rotateMethod:  time, size, stdout, stderr, none
+#... Note:  the following two parameters apply only when rotateMethod=time
+# timeRotateIntervalType:  S, M, H, D, W0 - W6, or midnight  (seconds, minutes, hours, days, weekday (0=Monday), or midnight UTC)
+# timeRotateInterval:  >= 1  (1 means every timeRotateIntervalType, 2 every other, 3 every third, etc.)
+#... Note:  the following parameter applies only when rotateMethod=size
+# sizeMaxBytes:  >= 0  (0 means no limit, else maximum filesize in Bytes)
+# backupCount:  >= 0  (Number of rotated backup files to retain.  If rotateMethod=time, 0 retains *all* backups.  If rotateMethod=size, 0 retains *no* backups.)
+#
+rotateMethod           = time
+timeRotateIntervalType = midnight
+timeRotateInterval     = 1
+sizeMaxBytes           = 0
+backupCount            = 6
+
+
+#--- Parameters that define log filenames and their initial LogLevel threshold
+#... Note:  CommonLogger will exit if your process does not have permission to write to the file.
+#
+
+error           = logs/error.log
+errorLogLevel   = WARN
+errorStyle      = error
+
+metrics         = logs/metrics.log
+metricsLogLevel = INFO
+metricsStyle    = metrics
+
+audit           = logs/audit.log
+auditLogLevel   = INFO
+auditStyle      = audit
+
+debug           = logs/debug.log
+debugLogLevel   = DEBUG
+debugStyle      = debug