added more robust logging tools, reconnect options to client js, and angular v1 module

This commit is contained in:
raz-varren
2017-05-17 19:32:46 +00:00
parent 131e7846aa
commit dbae76f803
7 changed files with 682 additions and 34 deletions

63
client/ng-ss.js Normal file
View File

@@ -0,0 +1,63 @@
(function(window){ 'use strict';
if(!window.angular){
throw 'angular not loaded';
return;
}
if(!window.SS){
throw 'sacrificial socket not loaded';
return;
}
window.angular.module('sacrificial-socket', [])
.factory('ss', ['$window', '$rootScope', '$log', function($window, $rootScope, $log){
function SSNG(url, opts){
var self = this,
socket = new $window.SS(url, opts);
self.onConnect = function(callback){
callback = callback || socket.noop;
socket.onConnect(function(){
var args = arguments;
$rootScope.$apply(function(){
callback.apply(self, args);
})
});
};
self.onDisconnect = function(callback){
callback = callback || socket.noop;
socket.onDisconnect(function(){
var args = arguments;
$rootScope.$apply(function(){
callback.apply(self, args);
});
});
};
self.on = function(eventName, callback){
callback = callback || socket.noop;
socket.on(eventName, function(){
var args = arguments;
$rootScope.$apply(function(){
callback.apply(self, args);
});
});
};
self.off = function(eventName){
return socket.off(eventName);
};
self.emit = function(eventName, data){
return socket.emit(eventName, data);
};
self.close = function(){
return socket.close();
};
}
return function(url, opts){ return new SSNG(url, opts); };
}]);
})(window);

View File

@@ -5,15 +5,40 @@
* @class SS * @class SS
* @constructor * @constructor
* @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss") * @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss")
* @param {Object} opts - connection options
*
* Default opts = {
* reconnectOpts: {
* enabled: true,
* replayOnConnect: true,
* intervalMS: 5000
* }
* }
*
*/ */
var SS = function(url){ var SS = function(url, opts){
opts = opts || {};
var self = this, var self = this,
ws = new WebSocket(url, 'sac-sock'),
events = {}, events = {},
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
reconnecting = false,
connectedOnce = false,
headerStartCharCode = 1, headerStartCharCode = 1,
headerStartChar = String.fromCharCode(headerStartCharCode), headerStartChar = String.fromCharCode(headerStartCharCode),
dataStartCharCode = 2, dataStartCharCode = 2,
dataStartChar = String.fromCharCode(dataStartCharCode); dataStartChar = String.fromCharCode(dataStartCharCode),
ws = new WebSocket(url, 'sac-sock');
//we really only support reconnect options for now
if(typeof opts.reconnectOpts == 'object'){
for(var i in opts.reconnectOpts){
if(!opts.reconnectOpts.hasOwnProperty(i)) continue;
reconnectOpts[i] = opts.reconnectOpts[i];
}
}
self.noop = function(){ };
//sorry, only supporting arraybuffer at this time //sorry, only supporting arraybuffer at this time
//maybe if there is demand for it, I'll add Blob support //maybe if there is demand for it, I'll add Blob support
@@ -68,29 +93,74 @@
if(eventName.length === 0) return; //no event to dispatch if(eventName.length === 0) return; //no event to dispatch
if(typeof events[eventName] === 'undefined') return; if(typeof events[eventName] === 'undefined') return;
events[eventName]((headers.J) ? JSON.parse(data) : data); events[eventName].call(self, (headers.J) ? JSON.parse(data) : data);
}; };
/**
* startReconnect is an internal function for reconnecting after an unexpected disconnect
*
* @function startReconnect
*
*/
function startReconnect(){
setTimeout(function(){
console.log('attempting reconnect');
var newWS = new WebSocket(url, 'sac-sock');
newWS.onmessage = ws.onmessage;
newWS.onclose = ws.onclose;
newWS.binaryType = ws.binaryType;
//we need to run the initially set onConnect function on first successful connect,
//even if replayOnConnect is disabled. The server might not be available on first
//connection attempt.
if(reconnectOpts.replayOnConnect || !connectedOnce){
newWS.onopen = ws.onopen;
}
ws = newWS;
if(!reconnectOpts.replayOnConnect && connectedOnce){
self.onConnect(self.noop);
}
}, reconnectOpts.intervalMS);
}
/** /**
* onConnect registers a callback to be run when the websocket connection is open. * onConnect registers a callback to be run when the websocket connection is open.
* *
* @method onConnect * @method onConnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection opens. * @param {Function} callback(event) - The callback that will be executed when the websocket connection opens.
* *
*/ */
self.onConnect = function(callback){ self.onConnect = function(callback){
ws.onopen = function(){ callback(self); }; ws.onopen = function(){
connectedOnce = true;
var args = arguments;
callback.apply(self, args);
if(reconnecting){
reconnecting = false;
}
}; };
};
self.onConnect(self.noop);
/** /**
* onDisconnect registers a callback to be run when the websocket connection is closed. * onDisconnect registers a callback to be run when the websocket connection is closed.
* *
* @method onDisconnect * @method onDisconnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection is closed. * @param {Function} callback(event) - The callback that will be executed when the websocket connection is closed.
*/ */
self.onDisconnect = function(callback){ self.onDisconnect = function(callback){
ws.onclose = function(){ callback(self); }; ws.onclose = function(){
var args = arguments;
if(!reconnecting && connectedOnce){
callback.apply(self, args);
}
if(reconnectOpts.enabled){
reconnecting = true;
startReconnect();
}
}; };
};
self.onDisconnect(self.noop);
/** /**
* on registers an event to be called when the client receives an emit from the server for * on registers an event to be called when the client receives an emit from the server for
@@ -162,6 +232,7 @@
* @method close * @method close
*/ */
self.close = function(){ self.close = function(){
reconnectOpts.enabled = false; //don't reconnect if close is called
return ws.close(); return ws.close();
}; };
}; };

View File

@@ -5,15 +5,40 @@
* @class SS * @class SS
* @constructor * @constructor
* @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss") * @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss")
* @param {Object} opts - connection options
*
* Default opts = {
* reconnectOpts: {
* enabled: true,
* replayOnConnect: true,
* intervalMS: 5000
* }
* }
*
*/ */
var SS = function(url){ var SS = function(url, opts){
opts = opts || {};
var self = this, var self = this,
ws = new WebSocket(url, 'sac-sock'),
events = {}, events = {},
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
reconnecting = false,
connectedOnce = false,
headerStartCharCode = 1, headerStartCharCode = 1,
headerStartChar = String.fromCharCode(headerStartCharCode), headerStartChar = String.fromCharCode(headerStartCharCode),
dataStartCharCode = 2, dataStartCharCode = 2,
dataStartChar = String.fromCharCode(dataStartCharCode); dataStartChar = String.fromCharCode(dataStartCharCode),
ws = new WebSocket(url, 'sac-sock');
//we really only support reconnect options for now
if(typeof opts.reconnectOpts == 'object'){
for(var i in opts.reconnectOpts){
if(!opts.reconnectOpts.hasOwnProperty(i)) continue;
reconnectOpts[i] = opts.reconnectOpts[i];
}
}
self.noop = function(){ };
//sorry, only supporting arraybuffer at this time //sorry, only supporting arraybuffer at this time
//maybe if there is demand for it, I'll add Blob support //maybe if there is demand for it, I'll add Blob support
@@ -68,29 +93,74 @@
if(eventName.length === 0) return; //no event to dispatch if(eventName.length === 0) return; //no event to dispatch
if(typeof events[eventName] === 'undefined') return; if(typeof events[eventName] === 'undefined') return;
events[eventName]((headers.J) ? JSON.parse(data) : data); events[eventName].call(self, (headers.J) ? JSON.parse(data) : data);
}; };
/**
* startReconnect is an internal function for reconnecting after an unexpected disconnect
*
* @function startReconnect
*
*/
function startReconnect(){
setTimeout(function(){
console.log('attempting reconnect');
var newWS = new WebSocket(url, 'sac-sock');
newWS.onmessage = ws.onmessage;
newWS.onclose = ws.onclose;
newWS.binaryType = ws.binaryType;
//we need to run the initially set onConnect function on first successful connect,
//even if replayOnConnect is disabled. The server might not be available on first
//connection attempt.
if(reconnectOpts.replayOnConnect || !connectedOnce){
newWS.onopen = ws.onopen;
}
ws = newWS;
if(!reconnectOpts.replayOnConnect && connectedOnce){
self.onConnect(self.noop);
}
}, reconnectOpts.intervalMS);
}
/** /**
* onConnect registers a callback to be run when the websocket connection is open. * onConnect registers a callback to be run when the websocket connection is open.
* *
* @method onConnect * @method onConnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection opens. * @param {Function} callback(event) - The callback that will be executed when the websocket connection opens.
* *
*/ */
self.onConnect = function(callback){ self.onConnect = function(callback){
ws.onopen = function(){ callback(self); }; ws.onopen = function(){
connectedOnce = true;
var args = arguments;
callback.apply(self, args);
if(reconnecting){
reconnecting = false;
}
}; };
};
self.onConnect(self.noop);
/** /**
* onDisconnect registers a callback to be run when the websocket connection is closed. * onDisconnect registers a callback to be run when the websocket connection is closed.
* *
* @method onDisconnect * @method onDisconnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection is closed. * @param {Function} callback(event) - The callback that will be executed when the websocket connection is closed.
*/ */
self.onDisconnect = function(callback){ self.onDisconnect = function(callback){
ws.onclose = function(){ callback(self); }; ws.onclose = function(){
var args = arguments;
if(!reconnecting && connectedOnce){
callback.apply(self, args);
}
if(reconnectOpts.enabled){
reconnecting = true;
startReconnect();
}
}; };
};
self.onDisconnect(self.noop);
/** /**
* on registers an event to be called when the client receives an emit from the server for * on registers an event to be called when the client receives an emit from the server for
@@ -162,6 +232,7 @@
* @method close * @method close
*/ */
self.close = function(){ self.close = function(){
reconnectOpts.enabled = false; //don't reconnect if close is called
return ws.close(); return ws.close();
}; };
}; };

View File

@@ -5,15 +5,40 @@
* @class SS * @class SS
* @constructor * @constructor
* @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss") * @param {String} url - The url to the sacrificial-socket server endpoint. The url must conform to the websocket URI Scheme ("ws" or "wss")
* @param {Object} opts - connection options
*
* Default opts = {
* reconnectOpts: {
* enabled: true,
* replayOnConnect: true,
* intervalMS: 5000
* }
* }
*
*/ */
var SS = function(url){ var SS = function(url, opts){
opts = opts || {};
var self = this, var self = this,
ws = new WebSocket(url, 'sac-sock'),
events = {}, events = {},
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
reconnecting = false,
connectedOnce = false,
headerStartCharCode = 1, headerStartCharCode = 1,
headerStartChar = String.fromCharCode(headerStartCharCode), headerStartChar = String.fromCharCode(headerStartCharCode),
dataStartCharCode = 2, dataStartCharCode = 2,
dataStartChar = String.fromCharCode(dataStartCharCode); dataStartChar = String.fromCharCode(dataStartCharCode),
ws = new WebSocket(url, 'sac-sock');
//we really only support reconnect options for now
if(typeof opts.reconnectOpts == 'object'){
for(var i in opts.reconnectOpts){
if(!opts.reconnectOpts.hasOwnProperty(i)) continue;
reconnectOpts[i] = opts.reconnectOpts[i];
}
}
self.noop = function(){ };
//sorry, only supporting arraybuffer at this time //sorry, only supporting arraybuffer at this time
//maybe if there is demand for it, I'll add Blob support //maybe if there is demand for it, I'll add Blob support
@@ -68,29 +93,74 @@
if(eventName.length === 0) return; //no event to dispatch if(eventName.length === 0) return; //no event to dispatch
if(typeof events[eventName] === 'undefined') return; if(typeof events[eventName] === 'undefined') return;
events[eventName]((headers.J) ? JSON.parse(data) : data); events[eventName].call(self, (headers.J) ? JSON.parse(data) : data);
}; };
/**
* startReconnect is an internal function for reconnecting after an unexpected disconnect
*
* @function startReconnect
*
*/
function startReconnect(){
setTimeout(function(){
console.log('attempting reconnect');
var newWS = new WebSocket(url, 'sac-sock');
newWS.onmessage = ws.onmessage;
newWS.onclose = ws.onclose;
newWS.binaryType = ws.binaryType;
//we need to run the initially set onConnect function on first successful connect,
//even if replayOnConnect is disabled. The server might not be available on first
//connection attempt.
if(reconnectOpts.replayOnConnect || !connectedOnce){
newWS.onopen = ws.onopen;
}
ws = newWS;
if(!reconnectOpts.replayOnConnect && connectedOnce){
self.onConnect(self.noop);
}
}, reconnectOpts.intervalMS);
}
/** /**
* onConnect registers a callback to be run when the websocket connection is open. * onConnect registers a callback to be run when the websocket connection is open.
* *
* @method onConnect * @method onConnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection opens. * @param {Function} callback(event) - The callback that will be executed when the websocket connection opens.
* *
*/ */
self.onConnect = function(callback){ self.onConnect = function(callback){
ws.onopen = function(){ callback(self); }; ws.onopen = function(){
connectedOnce = true;
var args = arguments;
callback.apply(self, args);
if(reconnecting){
reconnecting = false;
}
}; };
};
self.onConnect(self.noop);
/** /**
* onDisconnect registers a callback to be run when the websocket connection is closed. * onDisconnect registers a callback to be run when the websocket connection is closed.
* *
* @method onDisconnect * @method onDisconnect
* @param {Function} callback(SS) - The callback that will be executed when the websocket connection is closed. * @param {Function} callback(event) - The callback that will be executed when the websocket connection is closed.
*/ */
self.onDisconnect = function(callback){ self.onDisconnect = function(callback){
ws.onclose = function(){ callback(self); }; ws.onclose = function(){
var args = arguments;
if(!reconnecting && connectedOnce){
callback.apply(self, args);
}
if(reconnectOpts.enabled){
reconnecting = true;
startReconnect();
}
}; };
};
self.onDisconnect(self.noop);
/** /**
* on registers an event to be called when the client receives an emit from the server for * on registers an event to be called when the client receives an emit from the server for
@@ -162,6 +232,7 @@
* @method close * @method close
*/ */
self.close = function(){ self.close = function(){
reconnectOpts.enabled = false; //don't reconnect if close is called
return ws.close(); return ws.close();
}; };
}; };

152
log/json.go Normal file
View File

@@ -0,0 +1,152 @@
package log
import (
"encoding/json"
"fmt"
"io"
"os"
"runtime"
"strings"
"time"
)
type jLog struct {
Timestamp time.Time `json:"ts"`
File string `json:"file"`
Line int `json:"line"`
Level string `json:"level"`
Fatal bool `json:"fatal"`
Msg string `json:"msg"`
}
func (jl *jLog) Marshal() []byte {
data, err := json.Marshal(jl)
if err != nil {
return []byte(fmt.Sprintf(`{"Error":"%s", "Line": %d, "File":"%s"}`, err, jl.Line, jl.File))
}
return data
}
type jsonLogFmt struct {
out io.Writer
logLevel int
logLevelAllowed int
}
func (j *jsonLogFmt) canLog() bool {
return (j.logLevel & j.logLevelAllowed) == j.logLevelAllowed
}
func (j *jsonLogFmt) writeJLog(msg string, fatal bool) {
jl := newJLog(msg, LogLevelMap[j.logLevelAllowed], fatal, 3)
data := jl.Marshal()
j.out.Write(append(data, '\n'))
}
func newJLog(msg, level string, fatal bool, callDepth int) *jLog {
jl := &jLog{
Timestamp: time.Now(),
Level: level,
Fatal: fatal,
Msg: msg,
}
_, file, line, ok := runtime.Caller(callDepth)
jl.File = file
jl.Line = line
if !ok {
jl.File = "???"
jl.Line = -1
}
return jl
}
func (j *jsonLogFmt) Print(v ...interface{}) {
if !j.canLog() {
return
}
msg, fatal := fmt.Sprint(v...), false
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
}
func (j *jsonLogFmt) Printf(format string, v ...interface{}) {
if !j.canLog() {
return
}
msg, fatal := fmt.Sprintf(format, v...), false
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
}
func (j *jsonLogFmt) Println(v ...interface{}) {
if !j.canLog() {
return
}
msg, fatal := fmt.Sprintln(v...), false
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
}
func (j *jsonLogFmt) Fatal(v ...interface{}) {
msg, fatal := fmt.Sprint(v...), true
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
os.Exit(1)
}
func (j *jsonLogFmt) Fatalf(format string, v ...interface{}) {
msg, fatal := fmt.Sprintf(format, v...), true
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
os.Exit(1)
}
func (j *jsonLogFmt) Fatalln(v ...interface{}) {
msg, fatal := fmt.Sprintln(v...), true
msg = strings.TrimRight(msg, "\n")
j.writeJLog(msg, fatal)
os.Exit(1)
}
func NewJSONLogger(out io.Writer, logLevel int) *Logger {
info := &jsonLogFmt{
out: out,
logLevel: logLevel,
logLevelAllowed: LogLevelINFO,
}
warn := &jsonLogFmt{
out: out,
logLevel: logLevel,
logLevelAllowed: LogLevelWARN,
}
err := &jsonLogFmt{
out: out,
logLevel: logLevel,
logLevelAllowed: LogLevelERR,
}
debug := &jsonLogFmt{
out: out,
logLevel: logLevel,
logLevelAllowed: LogLevelDEBUG,
}
return &Logger{
Info: info,
Warn: warn,
Err: err,
Debug: debug,
}
}

View File

@@ -1,19 +1,62 @@
/*
Package log is used all throughout Sacrificial Socket for logging info and error messages
*/
package log package log
import ( import (
l "log"
"os" "os"
) )
var Err = l.New(os.Stderr, "ERROR: ", l.Ldate|l.Ltime|l.Lshortfile) const (
var Info = l.New(os.Stdout, "INFO: ", l.Ldate|l.Ltime) LogLevelINFO = 1 << iota
LogLevelWARN
LogLevelERR
LogLevelDEBUG
func CheckFatal(err error) { LogLevelNone = 0
if err != nil { LogLevelStd = LogLevelINFO | LogLevelWARN | LogLevelERR
Err.Output(2, err.Error()) LogLevelWarn = LogLevelWARN | LogLevelERR
os.Exit(1) LogLevelErr = LogLevelERR
LogLevelDbg = LogLevelStd | LogLevelDEBUG
)
var (
defaultLogger = NewColorLogger(os.Stdout, LogLevelDbg)
Info = defaultLogger.Info
Warn = defaultLogger.Warn
Err = defaultLogger.Err
Debug = defaultLogger.Debug
LogLevelMap = map[int]string{
LogLevelINFO: "INFO",
LogLevelWARN: "WARN",
LogLevelERR: "ERROR",
LogLevelDEBUG: "DEBUG",
} }
)
//this is pretty much the only thing that isn't
//safe to run in multiple go routines, you should
//call SetDefaultLogger at the beginning of your main function
func SetDefaultLogger(l *Logger) {
defaultLogger = l
Info = defaultLogger.Info
Warn = defaultLogger.Warn
Err = defaultLogger.Err
Debug = defaultLogger.Debug
}
type LogFormatter interface {
Fatal(v ...interface{})
Fatalf(format string, v ...interface{})
Fatalln(v ...interface{})
Print(v ...interface{})
Printf(format string, v ...interface{})
Println(v ...interface{})
}
type Logger struct {
Info LogFormatter
Warn LogFormatter
Err LogFormatter
Debug LogFormatter
} }

177
log/std-out.go Normal file
View File

@@ -0,0 +1,177 @@
package log
import (
"bytes"
"fmt"
"io"
goLog "log"
"os"
)
const (
colorLogDepth = 4
)
var (
ansiColorPallet = map[string][]byte{
"none": []byte("\x1b[0m"),
"black": []byte("\x1b[0;30m"),
"red": []byte("\x1b[0;31m"),
"green": []byte("\x1b[0;32m"),
"orange": []byte("\x1b[0;33m"),
"blue": []byte("\x1b[0;34m"),
"purple": []byte("\x1b[0;35m"),
"cyan": []byte("\x1b[0;36m"),
"light-gray": []byte("\x1b[0;37m"),
"dark-gray": []byte("\x1b[1;30m"),
"light-red": []byte("\x1b[1;31m"),
"light-green": []byte("\x1b[1;32m"),
"yellow": []byte("\x1b[1;33m"),
"light-blue": []byte("\x1b[1;34m"),
"light-purple": []byte("\x1b[1;35m"),
"light-cyan": []byte("\x1b[1;36m"),
"white": []byte("\x1b[1;37m"),
}
logColors = map[int][]byte{
LogLevelINFO: ansiColorPallet["white"],
LogLevelWARN: ansiColorPallet["orange"],
LogLevelERR: ansiColorPallet["red"],
LogLevelDEBUG: ansiColorPallet["light-blue"],
}
)
func NewColorLogger(out io.Writer, logLevel int) *Logger {
return newLogger(out, logLevel, true)
}
func NewLogger(out io.Writer, logLevel int) *Logger {
return newLogger(out, logLevel, false)
}
func newLogger(out io.Writer, logLevel int, color bool) *Logger {
logger := &Logger{
Info: newColorLogFmt(out, logLevel, LogLevelINFO, color),
Warn: newColorLogFmt(out, logLevel, LogLevelWARN, color),
Err: newColorLogFmt(out, logLevel, LogLevelERR, color),
Debug: newColorLogFmt(out, logLevel, LogLevelDEBUG, color),
}
return logger
}
type colorLogFmt struct {
out io.Writer
logger *goLog.Logger
logLevel int
fmtLevel int
color bool
}
func newColorLogFmt(out io.Writer, logLevel, fmtLevel int, color bool) *colorLogFmt {
clf := &colorLogFmt{
logLevel: logLevel,
fmtLevel: fmtLevel,
out: out,
color: color,
}
lFlags := goLog.LstdFlags
if fmtLevel != LogLevelINFO {
lFlags |= goLog.Lshortfile
}
clf.logger = goLog.New(out, padStr(LogLevelMap[fmtLevel], 6), lFlags)
return clf
}
func (lf *colorLogFmt) canLog() bool {
return (lf.logLevel & lf.fmtLevel) == lf.fmtLevel
}
func (lf *colorLogFmt) doOutput(pType, format string, v ...interface{}) {
switch pType {
case "Print", "Fatal":
lf.logger.Output(colorLogDepth, fmt.Sprint(v...))
case "Printf", "Fatalf":
lf.logger.Output(colorLogDepth, fmt.Sprintf(format, v...))
case "Println", "Fatalln":
lf.logger.Output(colorLogDepth, fmt.Sprintln(v...))
default:
lf.logger.Output(colorLogDepth, fmt.Sprint(v...))
}
}
func (lf *colorLogFmt) doPrint(pType, format string, v ...interface{}) {
switch pType {
case "Fatal", "Fatalf", "Fatalln":
lf.setErrColor()
lf.doOutput(pType, format, v...)
lf.dropColor()
os.Exit(1)
case "Print", "Printf", "Println":
if !lf.canLog() {
return
}
lf.setFmtColor()
lf.doOutput(pType, format, v...)
lf.dropColor()
}
}
//Fatal logs don't care what the logLevel is. They print
//and exit with a satus of 1
func (lf *colorLogFmt) Fatal(v ...interface{}) {
lf.doPrint("Fatal", "", v...)
}
func (lf *colorLogFmt) Fatalf(format string, v ...interface{}) {
lf.doPrint("Fatalf", format, v...)
}
func (lf *colorLogFmt) Fatalln(v ...interface{}) {
lf.doPrint("Fatalln", "", v...)
}
func (lf *colorLogFmt) Print(v ...interface{}) {
lf.doPrint("Print", "", v...)
}
func (lf *colorLogFmt) Printf(format string, v ...interface{}) {
lf.doPrint("Printf", format, v...)
}
func (lf *colorLogFmt) Println(v ...interface{}) {
lf.doPrint("Println", "", v...)
}
func (lf *colorLogFmt) setColor(color []byte) {
if !lf.color {
return
}
lf.out.Write(color)
}
func (lf *colorLogFmt) setErrColor() {
lf.setColor(logColors[LogLevelERR])
}
func (lf *colorLogFmt) setFmtColor() {
lf.setColor(logColors[lf.fmtLevel])
}
func (lf *colorLogFmt) dropColor() {
lf.setColor(ansiColorPallet["none"])
}
func padStr(s string, length int) string {
b := bytes.NewBuffer(nil)
b.WriteString(s)
for {
if b.Len() >= length {
return b.String()
}
b.WriteString(" ")
}
}