From 1a00f8667c8dc38e2200cedad46a4691cba46e10 Mon Sep 17 00:00:00 2001
From: Hendrik Buschmeier <hbuschme@uni-bielefeld.de>
Date: Fri, 18 Dec 2015 14:38:56 +0100
Subject: [PATCH] Added ipaaca-logger script to ipaacatools.

---
 ipaacalib/python/src/ipaaca/util/logger.py | 303 +++++++++++++++++++++
 ipaacatools/scripts/ipaaca-logger          |  69 +++++
 2 files changed, 372 insertions(+)
 create mode 100644 ipaacalib/python/src/ipaaca/util/logger.py
 create mode 100755 ipaacatools/scripts/ipaaca-logger

diff --git a/ipaacalib/python/src/ipaaca/util/logger.py b/ipaacalib/python/src/ipaaca/util/logger.py
new file mode 100644
index 0000000..804985a
--- /dev/null
+++ b/ipaacalib/python/src/ipaaca/util/logger.py
@@ -0,0 +1,303 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of IPAACA, the
+#  "Incremental Processing Architecture
+#   for Artificial Conversational Agents".
+#
+# Copyright (c) 2009-2015 Social Cognitive Systems Group
+#                         CITEC, Bielefeld University
+#
+# http://opensource.cit-ec.de/projects/ipaaca/
+# http://purl.org/net/ipaaca
+#
+# This file may be licensed under the terms of of the
+# GNU Lesser General Public License Version 3 (the ``LGPL''),
+# or (at your option) any later version.
+#
+# Software distributed under the License is distributed
+# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
+# express or implied. See the LGPL for the specific language
+# governing rights and limitations.
+#
+# You should have received a copy of the LGPL along with this
+# program. If not, go to http://www.gnu.org/licenses/lgpl.html
+# or write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The development of this software was supported by the
+# Excellence Cluster EXC 277 Cognitive Interaction Technology.
+# The Excellence Cluster EXC 277 is a grant of the Deutsche
+# Forschungsgemeinschaft (DFG) in the context of the German
+# Excellence Initiative.
+
+from __future__ import division, print_function
+
+import datetime
+import subprocess
+import sys
+import threading
+import time
+import traceback
+import uuid
+
+import ipaaca
+
+
+__all__ = [
+	'logger_send_ipaaca_logs',
+	'logger_set_module_name',
+	'LOG_DEBUG',
+	'LOG_INFO',
+	'LOG_WARN',
+	'LOG_WARNING',
+	'LOG_ERROR',
+]
+
+LOGGER_LOCK = threading.RLock()
+
+MODULE_NAME = sys.argv[0]
+
+SEND_IPAACA_LOGS = True
+
+OUTPUT_BUFFER = None
+
+STANDARD_LOG_FILE_EXTENSION = '.log'
+
+LOG_MODES = ['append', 'timestamp', 'overwrite']
+
+
+def logger_set_log_filename(filename, existing=None):
+	global OUTPUT_BUFFER
+	if existing is not None and not existing in LOG_MODES:
+		raise Exception('Invalid log mode {mode} given. '
+			'Valid options are {options}.'.format(
+				mode=existing,
+				options=', '.join(LOG_MODES)))
+	with LOGGER_LOCK:
+		if OUTPUT_BUFFER is None:
+			OUTPUT_BUFFER = ipaaca.OutputBuffer('LogSender')
+		msg = ipaaca.Message('logcontrol')
+		msg.payload = {
+			'cmd': 'open_log_file',
+			'filename': filename}
+		if existing is not None:
+			msg.payload['existing'] = existing
+		OUTPUT_BUFFER.add(msg)
+
+
+def logger_set_module_name(name):
+	global MODULE_NAME
+	with LOGGER_LOCK:
+		MODULE_NAME = name
+
+
+def logger_send_ipaaca_logs(flag=True):
+	global SEND_IPAACA_LOGS
+	with LOGGER_LOCK:
+		SEND_IPAACA_LOGS = flag
+
+
+def LOG_IPAACA(lvl, text, now=0.0, fn='???', thread='???'):
+	global OUTPUT_BUFFER
+	uid = str(uuid.uuid4())[0:8]
+	with LOGGER_LOCK:
+		if OUTPUT_BUFFER is None:
+			OUTPUT_BUFFER = ipaaca.OutputBuffer('LogSender')
+		msg = ipaaca.Message('log')
+		msg.payload = {
+				'module': MODULE_NAME,
+				'function': fn,
+				'level': lvl,
+				'time':' %.3f'%now,
+				'thread': thread,
+				'uuid': uid,
+				'text': text,}
+		OUTPUT_BUFFER.add(msg)
+
+
+def LOG_CONSOLE(lvlstr, msg, fn_markup='', msg_markup='', now=0.0, fn='???', thread='???'):
+	for line in msg.split('\n'):
+		text = lvlstr+' '+thread+' '+fn_markup+fn+''+' '+msg_markup+unicode(line)+''
+		print(text)
+		fn = ' '*len(fn)
+
+
+def LOG_ERROR(msg, now=None):
+	now = time.time() if now is None else now
+	f = sys._getframe(1)
+	classprefix = (f.f_locals['self'].__class__.__name__+'.') if 'self' in f.f_locals else ''
+	fn = classprefix + f.f_code.co_name
+	thread = threading.current_thread().getName()
+	with LOGGER_LOCK:
+		if SEND_IPAACA_LOGS: LOG_IPAACA('ERROR', msg, now=now, fn=fn, thread=thread)
+		LOG_CONSOLE('[ERROR]', msg, fn_markup='', msg_markup='', now=now, fn=fn, thread=thread)
+
+
+def LOG_WARN(msg, now=None):
+	now = time.time() if now is None else now
+	f = sys._getframe(1)
+	classprefix = (f.f_locals['self'].__class__.__name__+'.') if 'self' in f.f_locals else ''
+	fn = classprefix + f.f_code.co_name
+	thread = threading.current_thread().getName()
+	with LOGGER_LOCK:
+		if SEND_IPAACA_LOGS: LOG_IPAACA('WARN', msg, now=now, fn=fn, thread=thread)
+		LOG_CONSOLE('[WARN] ', msg, fn_markup='', msg_markup='', now=now, fn=fn, thread=thread)
+
+
+LOG_WARNING = LOG_WARN
+
+
+def LOG_INFO(msg, now=None):
+	now = time.time() if now is None else now
+	f = sys._getframe(1)
+	classprefix = (f.f_locals['self'].__class__.__name__+'.') if 'self' in f.f_locals else ''
+	fn = classprefix + f.f_code.co_name
+	thread = threading.current_thread().getName()
+	with LOGGER_LOCK:
+		if SEND_IPAACA_LOGS: LOG_IPAACA('INFO', msg, now=now, fn=fn, thread=thread)
+		LOG_CONSOLE('[INFO] ', msg, now=now, fn=fn, thread=thread)
+
+
+def LOG_DEBUG(msg, now=None):
+	now = time.time() if now is None else now
+	f = sys._getframe(1)
+	classprefix = (f.f_locals['self'].__class__.__name__+'.') if 'self' in f.f_locals else ''
+	fn = classprefix + f.f_code.co_name
+	thread = threading.current_thread().getName()
+	with LOGGER_LOCK:
+		if SEND_IPAACA_LOGS: LOG_IPAACA('DEBUG', msg, now=now, fn=fn, thread=thread)
+		LOG_CONSOLE('[DEBUG]', msg, fn_markup='', msg_markup='', now=now, fn=fn, thread=thread)
+
+
+class LoggerComponent(object):
+
+	def __init__(self, filename, log_mode='append'):
+		self.ib = ipaaca.InputBuffer('Logger', ['log', 'logcontrol'])
+		self.ib.register_handler(self._logger_handle_iu_event)
+		self.logfile = None
+		self.log_mode = log_mode
+		self.open_logfile(filename)
+		if self.logfile is None:
+			print('Logging to console only ...')
+		print('Press Ctrl-C at any time to terminate the logger.')
+
+	def open_logfile(self, filename):
+		with LOGGER_LOCK:
+			if filename is None or filename.strip() == '':
+				print('No log file name specified, not opening one.')
+				self.close_logfile()
+			else:
+				new_logfile = None
+				try:
+					# create dir first
+					ret = subprocess.call(
+							'mkdir -p `dirname ' + filename + '`',
+							shell=True)
+					# proceed with dir+file
+					if self.log_mode == 'timestamp':
+						t = datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S')
+						if filename.endswith(STANDARD_LOG_FILE_EXTENSION):
+							# insert timestamp between filename and extension
+							# (only for standard extension)
+							filename = filename.replace(
+									STANDARD_LOG_FILE_EXTENSION,
+									'.' + t + STANDARD_LOG_FILE_EXTENSION)
+						else: # prepend timestamp
+							filename = t + '_' + filename
+					append_if_exist = not (self.log_mode == 'overwrite' or
+							self.log_mode == 'timestamp')
+					new_logfile = open(filename, 'a' if append_if_exist else 'w')
+					if self.logfile is not None:
+						text = u'Will now continue logging in log file ' + unicode(filename)
+						uid = str(uuid.uuid4())[0:8]
+						tim = time.time()
+						record = {
+							'uuid': uid, 
+							'time': tim,
+							'level': u'INFO',
+							'text': text,
+							'module': u'logger',
+							'function': u'LoggerComponent.open_logfile', 
+							'thread': '-', 
+							'logreceivedtime': tim}
+						self.logfile.write(unicode(record)+'\n')
+						self.logfile.close()
+					self.logfile = new_logfile
+					print('Logging to console and {filename} ...'.format(filename=filename))
+				except Exception as e:
+					print('Failed to open logfile {filename} for writing! Keeping previous configuration'.format(filename=filename))
+
+	def close_logfile(self):
+		if self.logfile is not None:
+			text = u'Closing of log file requested.'
+			uid = unicode(uuid.uuid4())[0:8]
+			tim = unicode(time.time())
+			record = {
+				'uuid': uid,
+				'time': tim,
+				'level': u'INFO',
+				'text': text,
+				'module': u'logger',
+				'function': u'LoggerComponent.close_logfile', 
+				'thread': u'-',
+				'logreceivedtime': tim}
+			self.logfile.write(unicode(record)+'\n')
+			self.logfile.close()
+			print('Closed current log file.')
+			self.logfile = None
+
+	def _logger_handle_iu_event(self, iu, event_type, own):
+		received_time = "%.3f" % time.time()
+		with LOGGER_LOCK:
+			try:
+				if iu.category == 'log':
+					pl = iu.payload
+					message = pl['text'] if 'text' in pl else '(No message.)'
+					uid = '????????' if 'uuid' not in pl else pl['uuid']
+					tim = '???' if 'time' not in pl else pl['time']
+					module = '???' if 'module' not in pl else pl['module']
+					function = '???' if 'function' not in pl else pl['function']
+					thread = '???' if 'thread' not in pl else pl['thread']
+					level = 'INFO' if 'level' not in pl else pl['level']
+					# dump to console
+					if level=='WARN':
+						level='WARNING'
+					if level not in ['DEBUG','INFO','WARNING','ERROR']:
+						level = 'INFO'
+					try:
+						print('%s %-8s {%s} {%s} {%s} %s'%(tim, ('['+level+']'), thread, module, function, message))
+					except:
+						print('Failed to format a string for printing!')
+					if self.logfile is not None:
+						try:
+							record = {
+								'uuid': uid, 
+								'time': tim, 
+								'level': level, 
+								'text': message,
+								'module': module,
+								'function': function, 
+								'thread': thread,
+								'logreceivedtime': received_time}
+							self.logfile.write(unicode(record) + '\n')
+						except:
+							print('Failed to write to logfile!')
+				elif iu.category == 'logcontrol':
+					cmd = iu.payload['cmd']
+					if cmd == 'open_log_file':
+						filename = iu.payload['filename'] if 'filename' in iu.payload else ''
+						if 'existing' in iu.payload:
+							log_mode_ = iu.payload['existing'].lower()
+							if log_mode_ not in LOG_MODES:
+								LOG_WARN(u'Value of "existing" should be "append", timestamp, or  "overwrite", continuing with mode {mode}'.format(mode=self.log_mode))
+							else:
+								self.log_mode = log_mode_
+						self.open_logfile(filename)
+					elif cmd == 'close_log_file':
+						self.close_logfile()
+					else:
+						LOG_WARN(u'Received unknown logcontrol command: '+unicode(cmd))
+			except Exception, e:
+				print('Exception while logging!')  # TODO write to file as well?
+				print(u'  '+unicode(traceback.format_exc()))
diff --git a/ipaacatools/scripts/ipaaca-logger b/ipaacatools/scripts/ipaaca-logger
new file mode 100755
index 0000000..230143c
--- /dev/null
+++ b/ipaacatools/scripts/ipaaca-logger
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of IPAACA, the
+#  "Incremental Processing Architecture
+#   for Artificial Conversational Agents".
+#
+# Copyright (c) 2009-2015 Social Cognitive Systems Group
+#                         CITEC, Bielefeld University
+#
+# http://opensource.cit-ec.de/projects/ipaaca/
+# http://purl.org/net/ipaaca
+#
+# This file may be licensed under the terms of of the
+# GNU Lesser General Public License Version 3 (the ``LGPL''),
+# or (at your option) any later version.
+#
+# Software distributed under the License is distributed
+# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
+# express or implied. See the LGPL for the specific language
+# governing rights and limitations.
+#
+# You should have received a copy of the LGPL along with this
+# program. If not, go to http://www.gnu.org/licenses/lgpl.html
+# or write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The development of this software was supported by the
+# Excellence Cluster EXC 277 Cognitive Interaction Technology.
+# The Excellence Cluster EXC 277 is a grant of the Deutsche
+# Forschungsgemeinschaft (DFG) in the context of the German
+# Excellence Initiative.
+
+from __future__ import division, print_function
+
+import sys
+import time
+
+import ipaaca
+import ipaaca.util.logger as ipaacalog
+
+
+def main(log_mode, filename=None):
+	ipaacalog.logger_send_ipaaca_logs(False)
+	il = ipaacalog.LoggerComponent(filename, log_mode)
+	try:
+		while True:
+			time.sleep(1)
+	except KeyboardInterrupt:
+		il.close_logfile()
+		print('Logging-Component closed by keyboard interrupt.')
+		sys.exit(0)
+
+
+if __name__ == '__main__':
+	iap = ipaaca.IpaacaArgumentParser(
+        'ipaaca-logger')
+	iap.add_argument(
+		'-m', '--log-mode', dest='log_mode',
+		choices=ipaacalog.LOG_MODES,
+		default='append',
+		help="set what to do when logfile exists "
+				"(default: 'append'; 'timestamp' adds timestamp in any case)")
+	iap.add_argument(
+		'filename', nargs='?',
+		metavar='FILE',
+		help='set name of logfile')
+	arguments = iap.parse_args()
+	main(arguments.log_mode, arguments.filename)
-- 
GitLab