refactored logger into a module

This commit is contained in:
Kevin Froman 2019-07-13 19:29:33 -05:00
parent 4256de5097
commit 9298b3716b
No known key found for this signature in database
GPG Key ID: 0D414D0FE405B63B
12 changed files with 410 additions and 323 deletions

View File

@ -1,253 +0,0 @@
'''
Onionr - P2P Microblogging Platform & Social network
This file handles all operations involving logging
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import re, sys, time, traceback, os
class colors:
'''
This class allows you to set the color if ANSI codes are supported
'''
reset='\033[0m'
bold='\033[01m'
disable='\033[02m'
underline='\033[04m'
reverse='\033[07m'
strikethrough='\033[09m'
invisible='\033[08m'
italics='\033[3m'
class fg:
black='\033[30m'
red='\033[31m'
green='\033[32m'
orange='\033[33m'
blue='\033[34m'
purple='\033[35m'
cyan='\033[36m'
lightgrey='\033[37m'
darkgrey='\033[90m'
lightred='\033[91m'
lightgreen='\033[92m'
yellow='\033[93m'
lightblue='\033[94m'
pink='\033[95m'
lightcyan='\033[96m'
class bg:
black='\033[40m'
red='\033[41m'
green='\033[42m'
orange='\033[43m'
blue='\033[44m'
purple='\033[45m'
cyan='\033[46m'
lightgrey='\033[47m'
@staticmethod
def filter(data):
return re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]').sub('', str(data))
'''
Use the bitwise operators to merge these settings
'''
USE_ANSI = 0b100
if os.name == 'nt':
USE_ANSI = 0b000
OUTPUT_TO_CONSOLE = 0b010
OUTPUT_TO_FILE = 0b001
LEVEL_DEBUG = 1
LEVEL_INFO = 2
LEVEL_WARN = 3
LEVEL_ERROR = 4
LEVEL_FATAL = 5
LEVEL_IMPORTANT = 6
_type = OUTPUT_TO_CONSOLE | USE_ANSI # the default settings for logging
_level = LEVEL_DEBUG # the lowest level to log
_outputfile = 'data/onionr.log' # the file to log to
def set_settings(type):
'''
Set the settings for the logger using bitwise operators
'''
global _type
_type = type
def get_settings():
'''
Get settings from the logger
'''
return _type
def set_level(level):
'''
Set the lowest log level to output
'''
global _level
_level = level
def get_level():
'''
Get the lowest log level currently being outputted
'''
return _level
def set_file(outputfile):
'''
Set the file to output to, if enabled
'''
global _outputfile
_outputfile = outputfile
def get_file():
'''
Get the file to output to
'''
return _outputfile
def raw(data, fd = sys.stdout, terminal = False):
'''
Outputs raw data to console without formatting
'''
if terminal and (get_settings() & OUTPUT_TO_CONSOLE):
try:
ts = fd.write('%s\n' % data)
except OSError:
pass
if get_settings() & OUTPUT_TO_FILE:
try:
with open(_outputfile, "a+") as f:
f.write(colors.filter(data) + '\n')
except OSError:
pass
def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True, terminal = False):
'''
Logs the data
prefix : The prefix to the output
data : The actual data to output
color : The color to output before the data
'''
curTime = ''
if timestamp:
curTime = time.strftime("%m-%d %H:%M:%S") + ' '
output = colors.reset + str(color) + ('[' + colors.bold + str(prefix) + colors.reset + str(color) + '] ' if prompt is True else '') + curTime + str(data) + colors.reset
if not get_settings() & USE_ANSI:
output = colors.filter(output)
raw(output, fd = fd, terminal = terminal)
def readline(message = ''):
'''
Takes in input from the console, not stored in logs
message: The message to display before taking input
'''
color = colors.fg.green + colors.bold
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
if not get_settings() & USE_ANSI:
output = colors.filter(output)
sys.stdout.write(output)
return input()
def confirm(default = 'y', message = 'Are you sure %s? '):
'''
Displays an "Are you sure" message, returns True for Y and False for N
message: The confirmation message, use %s for (y/n)
default: which to prefer-- y or n
'''
color = colors.fg.green + colors.bold
default = default.lower()
confirm = colors.bold
if default.startswith('y'):
confirm += '(Y/n)'
else:
confirm += '(y/N)'
confirm += colors.reset + color
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
if not get_settings() & USE_ANSI:
output = colors.filter(output)
sys.stdout.write(output.replace('%s', confirm))
inp = input().lower()
if 'y' in inp:
return True
if 'n' in inp:
return False
else:
return default == 'y'
# debug: when there is info that could be useful for debugging purposes only
def debug(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_DEBUG):
if get_level() <= level:
log('/', data, timestamp = timestamp, prompt = prompt, terminal = terminal)
if not error is None:
debug('Error: ' + str(error) + parse_error())
# info: when there is something to notify the user of, such as the success of a process
def info(data, timestamp = False, prompt = True, terminal = False, level = LEVEL_INFO):
if get_level() <= level:
log('+', data, colors.fg.green, timestamp = timestamp, prompt = prompt, terminal = terminal)
# warn: when there is a potential for something bad to happen
def warn(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_WARN):
if not error is None:
debug('Error: ' + str(error) + parse_error())
if get_level() <= level:
log('!', data, colors.fg.orange, timestamp = timestamp, prompt = prompt, terminal = terminal)
# error: when only one function, module, or process of the program encountered a problem and must stop
def error(data, error = None, timestamp = True, prompt = True, terminal = False, level = LEVEL_ERROR):
if get_level() <= level:
log('-', data, colors.fg.red, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
if not error is None:
debug('Error: ' + str(error) + parse_error())
# fatal: when the something so bad has happened that the program must stop
def fatal(data, error = None, timestamp=True, prompt = True, terminal = False, level = LEVEL_FATAL):
if not error is None:
debug('Error: ' + str(error) + parse_error(), terminal = terminal)
if get_level() <= level:
log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
# returns a formatted error message
def parse_error():
details = traceback.extract_tb(sys.exc_info()[2])
output = ''
for line in details:
output += '\n ... module %s in %s:%i' % (line[2], line[0], line[1])
return output

71
onionr/logger/__init__.py Executable file
View File

@ -0,0 +1,71 @@
'''
Onionr - Private P2P Communication
This file handles all operations involving logging
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys, traceback
from . import colors, readline, log, raw, confirm, colors, settings
colors = colors.Colors
readline = readline.readline
log = log.log
raw = raw.raw
confirm = confirm.confirm
# debug: when there is info that could be useful for debugging purposes only
def debug(data, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_DEBUG):
if settings.get_level() <= level:
log('/', data, timestamp = timestamp, prompt = prompt, terminal = terminal)
if not error is None:
debug('Error: ' + str(error) + parse_error())
# info: when there is something to notify the user of, such as the success of a process
def info(data, timestamp = False, prompt = True, terminal = False, level = settings.LEVEL_INFO):
if settings.get_level() <= level:
log('+', data, colors.fg.green, timestamp = timestamp, prompt = prompt, terminal = terminal)
# warn: when there is a potential for something bad to happen
def warn(data, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_WARN):
if not error is None:
debug('Error: ' + str(error) + parse_error())
if settings.get_level() <= level:
log('!', data, colors.fg.orange, timestamp = timestamp, prompt = prompt, terminal = terminal)
# error: when only one function, module, or process of the program encountered a problem and must stop
def error(data, error = None, timestamp = True, prompt = True, terminal = False, level = settings.LEVEL_ERROR):
if settings.get_level() <= level:
log('-', data, colors.fg.red, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
if not error is None:
debug('Error: ' + str(error) + parse_error())
# fatal: when the something so bad has happened that the program must stop
def fatal(data, error = None, timestamp=True, prompt = True, terminal = False, level = settings.LEVEL_FATAL):
if not error is None:
debug('Error: ' + str(error) + parse_error(), terminal = terminal)
if get_level() <= level:
log('#', data, colors.bg.red + colors.fg.green + colors.bold, timestamp = timestamp, fd = sys.stderr, prompt = prompt, terminal = terminal)
# returns a formatted error message
def parse_error():
details = traceback.extract_tb(sys.exc_info()[2])
output = ''
for line in details:
output += '\n ... module %s in %s:%i' % (line[2], line[0], line[1])
return output

60
onionr/logger/colors.py Normal file
View File

@ -0,0 +1,60 @@
'''
Onionr - Private P2P Communication
class to access ANSI control codes
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import re
class Colors:
'''
This class allows you to set the color if ANSI codes are supported
'''
reset='\033[0m'
bold='\033[01m'
disable='\033[02m'
underline='\033[04m'
reverse='\033[07m'
strikethrough='\033[09m'
invisible='\033[08m'
italics='\033[3m'
class fg:
black='\033[30m'
red='\033[31m'
green='\033[32m'
orange='\033[33m'
blue='\033[34m'
purple='\033[35m'
cyan='\033[36m'
lightgrey='\033[37m'
darkgrey='\033[90m'
lightred='\033[91m'
lightgreen='\033[92m'
yellow='\033[93m'
lightblue='\033[94m'
pink='\033[95m'
lightcyan='\033[96m'
class bg:
black='\033[40m'
red='\033[41m'
green='\033[42m'
orange='\033[43m'
blue='\033[44m'
purple='\033[45m'
cyan='\033[46m'
lightgrey='\033[47m'
@staticmethod
def filter(data):
return re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]').sub('', str(data))

54
onionr/logger/confirm.py Normal file
View File

@ -0,0 +1,54 @@
'''
Onionr - Private P2P Communication
confirm y/n cli prompt
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys
from . import colors, settings
colors = colors.Colors
def confirm(default = 'y', message = 'Are you sure %s? '):
'''
Displays an "Are you sure" message, returns True for Y and False for N
message: The confirmation message, use %s for (y/n)
default: which to prefer-- y or n
'''
color = colors.fg.green + colors.bold
default = default.lower()
confirm = colors.bold
if default.startswith('y'):
confirm += '(Y/n)'
else:
confirm += '(y/N)'
confirm += colors.reset + color
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
if not get_settings() & settings.USE_ANSI:
output = colors.filter(output)
sys.stdout.write(output.replace('%s', confirm))
inp = input().lower()
if 'y' in inp:
return True
if 'n' in inp:
return False
else:
return default == 'y'

38
onionr/logger/log.py Normal file
View File

@ -0,0 +1,38 @@
'''
Onionr - Private P2P Communication
god log function
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys, time
from . import colors, raw, settings
colors = colors.Colors
def log(prefix, data, color = '', timestamp=True, fd = sys.stdout, prompt = True, terminal = False):
'''
Logs the data
prefix : The prefix to the output
data : The actual data to output
color : The color to output before the data
'''
curTime = ''
if timestamp:
curTime = time.strftime("%m-%d %H:%M:%S") + ' '
output = colors.reset + str(color) + ('[' + colors.bold + str(prefix) + colors.reset + str(color) + '] ' if prompt is True else '') + curTime + str(data) + colors.reset
if not settings.get_settings() & settings.USE_ANSI:
output = colors.filter(output)
raw.raw(output, fd = fd, terminal = terminal)

38
onionr/logger/raw.py Normal file
View File

@ -0,0 +1,38 @@
'''
Onionr - Private P2P Communication
Output raw data to file or terminal
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys
from . import settings, colors
colors = colors.Colors
def raw(data, fd = sys.stdout, terminal = False):
'''
Outputs raw data to console without formatting
'''
if terminal and (settings.get_settings() & settings.OUTPUT_TO_CONSOLE):
try:
ts = fd.write('%s\n' % data)
except OSError:
pass
if settings.get_settings() & settings.OUTPUT_TO_FILE:
try:
with open(settings._outputfile, "a+") as f:
f.write(colors.filter(data) + '\n')
except OSError:
pass

37
onionr/logger/readline.py Normal file
View File

@ -0,0 +1,37 @@
'''
Onionr - Private P2P Communication
get a line of input from stdin
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys
from . import colors, settings
colors = colors.Colors
def readline(message = ''):
'''
Takes in input from the console, not stored in logs
message: The message to display before taking input
'''
color = colors.fg.green + colors.bold
output = colors.reset + str(color) + '... ' + colors.reset + str(message) + colors.reset
if not settings.get_settings() & USE_ANSI:
output = colors.filter(output)
sys.stdout.write(output)
return input()

83
onionr/logger/settings.py Normal file
View File

@ -0,0 +1,83 @@
'''
Onionr - Private P2P Communication
logger settings
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import os
data_home = os.environ.get('DATA_DIR', os.environ.get('DATA_DIR', 'data'))
# Use the bitwise operators to merge these settings
USE_ANSI = 0b100
if os.name == 'nt':
USE_ANSI = 0b000
OUTPUT_TO_CONSOLE = 0b010
OUTPUT_TO_FILE = 0b001
LEVEL_DEBUG = 1
LEVEL_INFO = 2
LEVEL_WARN = 3
LEVEL_ERROR = 4
LEVEL_FATAL = 5
LEVEL_IMPORTANT = 6
_type = OUTPUT_TO_CONSOLE | USE_ANSI # the default settings for logging
_level = LEVEL_DEBUG # the lowest level to log
_outputfile = '%s/onionr.log' % (data_home,) # the file to log to
def set_settings(type):
'''
Set the settings for the logger using bitwise operators
'''
global _type
_type = type
def get_settings():
'''
Get settings from the logger
'''
return _type
def set_level(level):
'''
Set the lowest log level to output
'''
global _level
_level = level
def get_level():
'''
Get the lowest log level currently being outputted
'''
return _level
def set_file(outputfile):
'''
Set the file to output to, if enabled
'''
global _outputfile
_outputfile = outputfile
def get_file():
'''
Get the file to output to
'''
return _outputfile

View File

@ -71,9 +71,6 @@ class Onionr:
if not self.dataDir.endswith('/'): if not self.dataDir.endswith('/'):
self.dataDir += '/' self.dataDir += '/'
# set log file
logger.set_file(os.environ.get('LOG_DIR', 'data') + '/onionr.log')
# Load global configuration data # Load global configuration data
data_exists = Onionr.setupConfig(self.dataDir, self) data_exists = Onionr.setupConfig(self.dataDir, self)

View File

@ -19,6 +19,7 @@
''' '''
import os, json import os, json
import config, logger import config, logger
from logger.settings import *
def setup_config(dataDir, o_inst = None): def setup_config(dataDir, o_inst = None):
data_exists = os.path.exists(dataDir) data_exists = os.path.exists(dataDir)
@ -40,48 +41,48 @@ def setup_config(dataDir, o_inst = None):
settings = 0b000 settings = 0b000
if config.get('log.console.color', True): if config.get('log.console.color', True):
settings = settings | logger.USE_ANSI settings = settings | USE_ANSI
if config.get('log.console.output', True): if config.get('log.console.output', True):
settings = settings | logger.OUTPUT_TO_CONSOLE settings = settings | OUTPUT_TO_CONSOLE
if config.get('log.file.output', True): if config.get('log.file.output', True):
settings = settings | logger.OUTPUT_TO_FILE settings = settings | OUTPUT_TO_FILE
logger.set_settings(settings) set_settings(settings)
if not o_inst is None: if not o_inst is None:
if str(config.get('general.dev_mode', True)).lower() == 'true': if str(config.get('general.dev_mode', True)).lower() == 'true':
o_inst._developmentMode = True o_inst._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG) set_level(LEVEL_DEBUG)
else: else:
o_inst._developmentMode = False o_inst._developmentMode = False
logger.set_level(logger.LEVEL_INFO) set_level(LEVEL_INFO)
verbosity = str(config.get('log.verbosity', 'default')).lower().strip() verbosity = str(config.get('log.verbosity', 'default')).lower().strip()
if not verbosity in ['default', 'null', 'none', 'nil']: if not verbosity in ['default', 'null', 'none', 'nil']:
map = { map = {
str(logger.LEVEL_DEBUG) : logger.LEVEL_DEBUG, str(LEVEL_DEBUG) : LEVEL_DEBUG,
'verbose' : logger.LEVEL_DEBUG, 'verbose' : LEVEL_DEBUG,
'debug' : logger.LEVEL_DEBUG, 'debug' : LEVEL_DEBUG,
str(logger.LEVEL_INFO) : logger.LEVEL_INFO, str(LEVEL_INFO) : LEVEL_INFO,
'info' : logger.LEVEL_INFO, 'info' : LEVEL_INFO,
'information' : logger.LEVEL_INFO, 'information' : LEVEL_INFO,
str(logger.LEVEL_WARN) : logger.LEVEL_WARN, str(LEVEL_WARN) : LEVEL_WARN,
'warn' : logger.LEVEL_WARN, 'warn' : LEVEL_WARN,
'warning' : logger.LEVEL_WARN, 'warning' : LEVEL_WARN,
'warnings' : logger.LEVEL_WARN, 'warnings' : LEVEL_WARN,
str(logger.LEVEL_ERROR) : logger.LEVEL_ERROR, str(LEVEL_ERROR) : LEVEL_ERROR,
'err' : logger.LEVEL_ERROR, 'err' : LEVEL_ERROR,
'error' : logger.LEVEL_ERROR, 'error' : LEVEL_ERROR,
'errors' : logger.LEVEL_ERROR, 'errors' : LEVEL_ERROR,
str(logger.LEVEL_FATAL) : logger.LEVEL_FATAL, str(LEVEL_FATAL) : LEVEL_FATAL,
'fatal' : logger.LEVEL_FATAL, 'fatal' : LEVEL_FATAL,
str(logger.LEVEL_IMPORTANT) : logger.LEVEL_IMPORTANT, str(LEVEL_IMPORTANT) : LEVEL_IMPORTANT,
'silent' : logger.LEVEL_IMPORTANT, 'silent' : LEVEL_IMPORTANT,
'quiet' : logger.LEVEL_IMPORTANT, 'quiet' : LEVEL_IMPORTANT,
'important' : logger.LEVEL_IMPORTANT 'important' : LEVEL_IMPORTANT
} }
if verbosity in map: if verbosity in map:
logger.set_level(map[verbosity]) set_level(map[verbosity])
else: else:
logger.warn('Verbosity level %s is not valid, using default verbosity.' % verbosity) logger.warn('Verbosity level %s is not valid, using default verbosity.' % verbosity)

View File

@ -68,7 +68,7 @@ fetch('/friends/list', {
var nameText = document.createElement('input') var nameText = document.createElement('input')
removeButton = document.createElement('button') removeButton = document.createElement('button')
removeButton.classList.add('friendRemove') removeButton.classList.add('friendRemove')
removeButton.classList.add('dangerBtn') removeButton.classList.add('button', 'is-danger')
entry.setAttribute('data-pubkey', peer) entry.setAttribute('data-pubkey', peer)
removeButton.innerText = 'X' removeButton.innerText = 'X'
nameText.value = name nameText.value = name

View File

@ -1,43 +1,4 @@
/*
h2, h3{
font-family: Arial, Helvetica, sans-serif;
}
form{
border: 1px solid black;
border-radius: 5px;
padding: 1em;
margin-right: 10%;
}
form label{
display: block;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
#friendList{
display: inline;
}
#friendList span{
text-align: center;
}
#friendList button{ #friendList button{
display: inline; display: inline;
margin-right: 10px; margin-right: 10px;
} }
#friendInfo .overlayContent{
background-color: lightgray;
border: 3px solid black;
border-radius: 3px;
color: black;
font-family: Verdana, Geneva, Tahoma, sans-serif;
min-height: 100%;
padding: 1em;
margin: 1em;
}
#defriend{
display: block;
margin-top: 1em;
}
*/