mirror of
https://github.com/raz-varren/sacrificial-socket.git
synced 2025-10-06 16:46:56 +08:00
added more robust logging tools, reconnect options to client js, and angular v1 module
This commit is contained in:
63
client/ng-ss.js
Normal file
63
client/ng-ss.js
Normal 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);
|
@@ -5,15 +5,40 @@
|
||||
* @class SS
|
||||
* @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 {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,
|
||||
ws = new WebSocket(url, 'sac-sock'),
|
||||
events = {},
|
||||
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
|
||||
reconnecting = false,
|
||||
connectedOnce = false,
|
||||
headerStartCharCode = 1,
|
||||
headerStartChar = String.fromCharCode(headerStartCharCode),
|
||||
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
|
||||
//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(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.
|
||||
*
|
||||
* @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){
|
||||
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.
|
||||
*
|
||||
* @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){
|
||||
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
|
||||
@@ -162,6 +232,7 @@
|
||||
* @method close
|
||||
*/
|
||||
self.close = function(){
|
||||
reconnectOpts.enabled = false; //don't reconnect if close is called
|
||||
return ws.close();
|
||||
};
|
||||
};
|
||||
|
@@ -5,15 +5,40 @@
|
||||
* @class SS
|
||||
* @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 {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,
|
||||
ws = new WebSocket(url, 'sac-sock'),
|
||||
events = {},
|
||||
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
|
||||
reconnecting = false,
|
||||
connectedOnce = false,
|
||||
headerStartCharCode = 1,
|
||||
headerStartChar = String.fromCharCode(headerStartCharCode),
|
||||
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
|
||||
//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(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.
|
||||
*
|
||||
* @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){
|
||||
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.
|
||||
*
|
||||
* @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){
|
||||
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
|
||||
@@ -162,6 +232,7 @@
|
||||
* @method close
|
||||
*/
|
||||
self.close = function(){
|
||||
reconnectOpts.enabled = false; //don't reconnect if close is called
|
||||
return ws.close();
|
||||
};
|
||||
};
|
||||
|
@@ -5,15 +5,40 @@
|
||||
* @class SS
|
||||
* @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 {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,
|
||||
ws = new WebSocket(url, 'sac-sock'),
|
||||
events = {},
|
||||
reconnectOpts = {enabled: true, replayOnConnect: true, intervalMS: 5000},
|
||||
reconnecting = false,
|
||||
connectedOnce = false,
|
||||
headerStartCharCode = 1,
|
||||
headerStartChar = String.fromCharCode(headerStartCharCode),
|
||||
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
|
||||
//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(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.
|
||||
*
|
||||
* @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){
|
||||
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.
|
||||
*
|
||||
* @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){
|
||||
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
|
||||
@@ -162,6 +232,7 @@
|
||||
* @method close
|
||||
*/
|
||||
self.close = function(){
|
||||
reconnectOpts.enabled = false; //don't reconnect if close is called
|
||||
return ws.close();
|
||||
};
|
||||
};
|
||||
|
152
log/json.go
Normal file
152
log/json.go
Normal 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,
|
||||
}
|
||||
}
|
63
log/log.go
63
log/log.go
@@ -1,19 +1,62 @@
|
||||
/*
|
||||
Package log is used all throughout Sacrificial Socket for logging info and error messages
|
||||
*/
|
||||
package log
|
||||
|
||||
import (
|
||||
l "log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Err = l.New(os.Stderr, "ERROR: ", l.Ldate|l.Ltime|l.Lshortfile)
|
||||
var Info = l.New(os.Stdout, "INFO: ", l.Ldate|l.Ltime)
|
||||
const (
|
||||
LogLevelINFO = 1 << iota
|
||||
LogLevelWARN
|
||||
LogLevelERR
|
||||
LogLevelDEBUG
|
||||
|
||||
func CheckFatal(err error) {
|
||||
if err != nil {
|
||||
Err.Output(2, err.Error())
|
||||
os.Exit(1)
|
||||
LogLevelNone = 0
|
||||
LogLevelStd = LogLevelINFO | LogLevelWARN | LogLevelERR
|
||||
LogLevelWarn = LogLevelWARN | LogLevelERR
|
||||
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
177
log/std-out.go
Normal 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(" ")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user