]> git.r.bdr.sh - rbdr/cologne/commitdiff
Recover from npm package
authorRuben Beltran del Rio <redacted>
Sun, 20 Sep 2020 17:47:28 +0000 (19:47 +0200)
committerRuben Beltran del Rio <redacted>
Sun, 20 Sep 2020 17:47:28 +0000 (19:47 +0200)
30 files changed:
.eslintignore [new file with mode: 0644]
.eslintrc [new file with mode: 0644]
.npmignore [new file with mode: 0644]
CHANGELOG
README.md
bower.json [deleted file]
config/jsdoc.json [new file with mode: 0644]
lib/cobalt.js [deleted file]
lib/cologne.js [new file with mode: 0644]
lib/cologne/formatter/simple.js [new file with mode: 0644]
lib/cologne/formatter/token.js [new file with mode: 0644]
lib/cologne/log_utilities.js [new file with mode: 0644]
lib/cologne/logger/console.js [new file with mode: 0644]
lib/cologne/logger/file.js [new file with mode: 0644]
lib/ext/socket_helper.js [deleted file]
lib/formatters/ansi.js [deleted file]
lib/formatters/simple.js [deleted file]
lib/formatters/token.js [deleted file]
lib/loggers/console.js [deleted file]
lib/loggers/file.js [deleted file]
lib/loggers/socket.js [deleted file]
package.json
test/basic.js [deleted file]
test/browser.html [deleted file]
test/cologne.js [new file with mode: 0644]
test/cologne/formatter/simple.js [new file with mode: 0644]
test/cologne/formatter/token.js [new file with mode: 0644]
test/cologne/log_utilities.js [new file with mode: 0644]
test/cologne/logger/console.js [new file with mode: 0644]
test/cologne/logger/file.js [new file with mode: 0644]

diff --git a/.eslintignore b/.eslintignore
new file mode 100644 (file)
index 0000000..8e695ec
--- /dev/null
@@ -0,0 +1 @@
+doc
diff --git a/.eslintrc b/.eslintrc
new file mode 100644 (file)
index 0000000..bb7120d
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,41 @@
+{
+  "rules": {
+    "indent": [
+      2,
+      2
+    ],
+    "quotes": [
+      2,
+      "single"
+    ],
+    "linebreak-style": [
+      2,
+      "unix"
+    ],
+    "semi": [
+      2,
+      "always"
+    ],
+    "no-trailing-spaces": [
+      2,
+      {
+        "skipBlankLines": false
+      }
+    ],
+    "eqeqeq": [
+      2,
+      "allow-null"
+    ],
+    "consistent-return": 2,
+    "curly": 2,
+    "dot-location": [2, "property"],
+    "guard-for-in": 2,
+    "no-extra-bind": 2
+  },
+  "env": {
+    "es6": true,
+    "node": true,
+    "browser": true
+  },
+  "extends": "eslint:recommended"
+}
diff --git a/.npmignore b/.npmignore
new file mode 100644 (file)
index 0000000..cc4bbcc
--- /dev/null
@@ -0,0 +1,37 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
+node_modules
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+# JSDoc
+doc
index 6f1064142da021edf204224438e728f7fa7cc42b..915f2e302aa2bedd3a0440982d7fd3a6cdad7020 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,52 +1,24 @@
-2015-01-21 Ben Beltran <ben@nsovocal.com>
-
-       Version 1.1.3
-
-       * lib/loggers/file.js: Makes the log work in append mode instead of
-       overwrite mode.
-
-2015-01-09 Sergio de la Garza <sergio@delagarza.io>
-
-       Version 1.1.2
-
-       * lib/cobalt.js (dependencies): Require colors fix the log output when
-       using the tocken formatter.
-
-
-2015-01-09 Ben Beltran <ben@nsovocal.com>
-
-       Version 1.1.1
-
-       * lib/loggers/file.js: Writes raw log if no formatter is present.
-
-       * lib/cobalt.js: Logging null now converts it to the string "null";
+2016-01-22 Ruben Beltran Del Rio <ben@nsovocal.com>
 
        Version 1.1.0
 
-       * lib/loggers/file.js: Includes the new file logger.
+       * lib/cologne.js (buildLog): Add a new parameter called meta that expects an
+       object, it will be used to extend the log.
 
-       * lib/formatters/simple.js (format): Simple formatter outputs date and
-       loglevel.
+       * lib/cologne/log_utilities.js: Add the getLevelAnsi method that will
+       return an ANSI keyword depending on the log level sent.
 
-       * lib/cobalt.js: Includes the file logger and simple formatter.
+       * lib/cologne/formatter/simple (format): Now uses getLevelAnsi, this means
+       that info and debug get colors (they used to be defualt.) info is blue,
+       debug is green.
 
-       * lib/formatters/token.js: Extends with options on
-       initialization, including a new option isoDate that will make the
-       date an ISOString.
+       * lib/cologne/formatter/token (format): Adds support for the {{_ansi:_level}}
+       token that uses getLevelAnsi to generate color depending on the level.
 
-       * README.md: Updates documentation to consider the new options, formatter
-       and logger.
+       * README.md: Adds references to all new features
 
-       
-2014-03-20 Ben Beltran <ben@nsovocal.com>
+2016-01-21 Ruben Beltran Del Rio <ben@nsovocal.com>
 
        Version 1.0.0
 
-       * lib/cobalt.js (buildLog): Logging a logObject will now use the calling
-       method's severity instead of the logObject severity. so calling `#error`
-       will always provide severity level 3.
-
-       * package.json: Updates neon dependency to 2.0, adds tellurium as
-       dev-dependency.
-
-       * /test: Adds VERY basic tests before tellurium is integrated.
+       * all: initial release.
index 87f808f339fdd48df5b6c081c7367c0e6044f4f3..bf2ae2d15be8c7aa18a5ae3e7e7e161119d2635b 100644 (file)
--- a/README.md
+++ b/README.md
-# Cobalt #
+# Cologne
 
-Cobalt is a simple logger multiplexer that works with a JSON based format.
-You can instantiate it with a set of loggers or add them later
-(see API reference below). When logging anything, Cobalt will attempt to
-generate an Object that conforms to the Cobalt Log Format Definition
-(if required) and passes it to every logger it has by calling their `log`
-method.
+![https://david-dm.org/rbdr/cologne.svg][dependencies-shield]
+![https://circleci.com/gh/rbdr/cologne.svg?style=shield][circle-ci-shield]
 
-Example of instantiating a cobalt logger:
+Cologne is a logger multiplexer that works mainly with a JSON format. It
+can be instantiated with several loggers, or they can be changed after
+the fact.
+
+## Usage
+
+Install from npm or add to your package.json:
 
-In node:
 ```
-require('cobalt-log');
+$ npm install cologne
 ```
 
-In the browser just require the files. Then:
+Create an instance:
 
-```
-this.logger = new Cobalt.Console({
-  from : "Breezi Client",
-  loggers : [ new Cobalt.Logger.JsConsole({
-              formatter : Cobalt.Formatter.Token,
-              formatterOpts : {
-                formatString : "{{_from}}: {{message}}",
-                ansiColor : true
-              }
-            })]
+```javascript
+require('cologne');
+
+let co = new Cologne({
+  from: "Special Worker Logger",
+  loggers: [
+    new Cologne.Logger.Console({
+      formatter: new Cologne.Formatter.Token({
+        formatString: '[{{_timestamp}}]{{_from}}: {{message}}'
+      })
+    })
+  ]
 });
 ```
 
-This code will create an instance with a JsConsole logger that uses the
-Token formatter (See loggers and formatters below).
+This example would create a cologne instance with a console logger that
+uses a Token formatter. (More on loggers and formatters below.);
 
-Cobalt works in a browser or inside node, so feel free to use cobalt all
-over! (Also, see the socket logger below for info on connecting cobalt
-loggers)
+## Do I need anything special?
 
-## Quick API Reference ##
+Node 4.0+
 
-* **addLogger(logger)**: Adds a logger to the cobalt instance.
-* **removeLogger(logger)**: Removes a logger from the cobalt instance.
-* **buildLog(item, level=7)**: Generates a cobalt log object as if you had
-  logged item (it will do this automatically when you log anything)
-* **extendLog(object)**: Creates a dummy log object and extends it with
-  object.
-* **buildSeparator**: Generates a cobalt log object that defines a separator
+## Quick API Reference
+
+* **addLogger(logger)**: Adds a logger to the cologne instance.
+* **removeLogger(logger)**: Removes a logger from the cologne instance.
+* **buildLog(item, level, [meta])**: Generates a cologne log object as if you had
+  logged an item (it will do this automatically when you log anything.)
+  level defaults to 6. You can optionally send it an object to extend
+  the object with.
 * **log, info, notice, warn, error**: Generates a log object with the
   appropriate severity level and sends it to all loggers.
-* **separator()**: Generates a separator log object and sends it to all
-  loggers.
-* **space(lines)**: Logs an empty string `lines` times
-* **indent()**: Increases the indent level globally.
-* **indent(callback)**: Increases the indent level for anything logged
-  from inside the callback.
-* **outdent()/outdent(callback)**: Same as indent, but decreases indent level.
-* **color()**: Changes the color globally. †
-* **color(callback)**: Changes the color for anything logged from inside the
-  callback. †
-* **now()**: Returns the current time in microseconds, using performance.now()
-  or process.hrtime() if available. If not, falls back to miliseconds.
-
-† Cobalt doesn't really care about formatting or colors, but it allows you
-to set the `_color` property in the generated object. In the end, it's up to
-the formatter to decide if it will use this property. However, this maintains
-the old cobalt API and gives you flexibility in how you color your logs.
-
-
-## Loggers ##
-
-Cobalt doesn't depend on any particular logger, and the loggers it expects
-to receive is any object that responds to the log method. However, since it
-would pass a JSON object instead of a string, this may result in unexpected
-behavior for loggers that don't expect it. To ease the use of Cobalt with
-existing loggers, cobalt includes a couple of loggers that you can use out
-of the box.
-
-
-### Cobalt.Logger.JsConsole ###
-
-This logger communicates the Javascript console present in web browsers or
-node with cobalt. It uses the logLevel to trigger the appropriate method
-(e.g. info vs warn vs error). You can also initialize it with a formatter,
-to convert the log object to a string:
 
-```
- new Cobalt.Logger.JsConsole({
-     formatter : Cobalt.Formatter.Token,
-     formatterOpts : {
-         formatString : "[{{_timestamp}}] {{message}} (@{{_from}})"
-     }
- }) 
-```
+## Loggers
 
-What this does is: it will trigger the method `format` on `formatter`
-passing the `logObject` and `formatterOpts`. This means that a formatter is
-any object that responds to `format(logObject, formatterOpts)`. It expects
-a string to be returned.
+Cologne loggers are any object that responds to the `#log()` method.
+This methoud should be able to receive any number of arguments and
+log them independently. Similar to how you can send multiple arguments
+to the JS console.
 
-### Cobalt.Logger.File ###
+`#log()` will receive any number of `Cologne Log Objects`. To see what
+this format includes, check further below.
 
-This logger communicates a file via a writable stream, and is intended
-only for node. Like the JSConsole logger, you can also initialize it with
-a formatter to convert the log object to a string:
+We include two loggers out of the box:
 
+* `Cologne.Logger.Console` logs to the JS console
+* `Cologne.Logger.File` appends to a file
+
+### Cologne.Logger.Console
+
+This logger communicates the Javascript console. It uses the log level
+to trigger the appropriate method, so `error` logs would go to stderr
+as expected when calling `console.error`.
+
+This logger can be sent a `formatter`, which is an object that responds
+to the `#format()` method: it should get a cologne log object and respond
+with a string.
+
+```javascript
+new Cologne.Logger.Console({
+  formatter : new Cologne.Formatter.Token({
+    formatString: '[{{_timestamp}}]{{_from}}: {{message}}'
+  })
+});
 ```
- new Cobalt.Logger.File({
-     formatter : Cobalt.Formatter.Token,
-     formatterOpts : {
-         formatString : "[{{_timestamp}}] {{message}} (@{{_from}})"
-     }
- }) 
-```
 
-What this does is: it will trigger the method `format` on `formatter`
-passing the `logObject` and `formatterOpts`. This means that a formatter is
-any object that responds to `format(logObject, formatterOpts)`. It expects
-a string to be returned.
+### Cologne.Logger.File
+
+This logger opens a writable stream to a file, to which it will append
+everything. Like the Console logger it supports a `formatter` property
+that will respond to the `#format()` method.
+
+It MUST include a `file` property on initialization, otherwise it won't
+know where to write and you'll get an exception and be sad.
+
+```javascript
+new Cologne.Logger.File({
+  file: '/var/log/server_log.log',
+  formatter : new Cologne.Formatter.Token({
+    formatString: '[{{_timestamp}}]{{_from}}: {{message}}'
+  })
+});
+```
 
-### Cobalt.Logger.Socket ###
+### More Loggers?
 
-This logger sends the log object to a socket using Socket.IO. It does not
-format the output. To catch the log from the recipient, you have to listen
-for the `log` event, and from there you can pass it to another Cobalt
-instance or do whatever you want with it.
+We're working on a socket logger. It's separate so you don't have to
+install the socket dependencies if you don't want to.
 
-### More Loggers? ###
+If you need to, you can roll your own. Check info on the interfaces
+below.
 
 You can build your own logger easily for any method of transport you find
 necessary (e.g. mail, database, twitter, etc). Any object that responds
-to `#log(logObject)` is a valid logger:
+to `#log()` is a valid logger:
 
 ```javascript
 // A valid, very minimalistic logger
-var simpleLogger = {
-  log : function (logObject) {
-    console.log(logObject.message);
+let simpleLogger = {
+  log : function () {
+    for (let logObject of arguments) {
+      this._doSomeMagic(logObject);
+    }
+  },
+
+  _doSomeMagic : function (logObject) {
+    console.log(logObject + "... but magical!");
   }
-}
+};
 
 logger.addLogger(simpleLogger);
 ```
 
-## Formatters ##
 
-Cobalt itself makes no assumptions about the output of the logger and just
-passes the object to every logger it has. However, it is clear that loggers
-may want to manipulate this object. As shown in the JsConsole, a formatter
-should respond to the format method and receive a `logObject` and an
-`optsObject`. However, as this is not a core part of Cobalt, this is only a
-recommendation (as this is the way the included JsConsole/File loggers do it)
-and it is up to the logger on how to transform the object it receives.
+## Formatters
+
+Cologne doesn't need formatters to work, and in fact they're optional in
+the included workers. But if you would like to make your logs prettier,
+then you can use one of the included formatters or roll your own.
+
+Formatters are objects that respond to the `#format()` method. It will
+receive a single cologne log object (see fields it includes below), and
+it should return a string. That's all there is to it.
 
-### Cobalt.Formatter.Simple ###
+We include some formatters so you can get running real quicklike:
+
+* `Cologne.Formatter.Simple` a simple predefined formatter
+* `Cologne.Formatter.Token` a formatter that lets you define format
+  strings that it will use to build your final log.
+
+### Cologne.Formatter.Simple
 
 This is the lazy formatter, it just outputs the string in the following
 format:
 
 ```
-'[{{_timestamp}}][{{_logLevelString}}]{{_from}}: {{_message}}'
+'[{{_timestamp}}][{{_levelString}}]{{_from}}: {{message}}'
 ```
 
 Where `_timestamp` is converted to ISO.
 
-Example output:
+#### Accepted Options
+
+* `colorize` <Boolean>: whether or not to add color. False by default.
+
+By default we don't colorize the output, but if you enable the flag this
+formatter will add a bit of color in the level string. Red for bad,
+yellow for warn, blue for info, and white for everything else.
 
+#### Usage
+
+```javascript
+new Cologne.Formatter.Simple({
+  colorize: true
+});
 ```
-cobalt.log("hello world");
-// -> [2015-01-09T16:02:23.102Z][INFO] Generic Cobalt Logger : hello world
+
+### Example Output
+
+```
+co.log("hello world");
+// -> [2016-01-21T05:50:36.505Z][INFO] Server Logger: hello world
 ```
 
-### Cobalt.Formatter.Token ###
+### Cologne.Formatter.Token
 
-The Token formatter is a more advanced, but still fairly simple
-formatter. It takes a `formatString` and interpolates the properties
-of the object. By default it transforms anything inside double curly
-braces `{{likeThis}}`, however you can set a custom `replaceRule`.
+The token formatter lets you build strings with simple tokens. When
+instantiating, you can specify a `formatString` to interpolate
+properties from the logObject. The default version looks for tokens
+inside double curly braces like `{{message}}` or `{{_level}}`. If
+you don't like it, you can specify your own.
 
-#### Accepted Options ####
+#### Accepted Options
 
-* `formatString` : The string used to replace. Defaults to `"{{message}}"`
-* `replaceRule` : The regex rule to use for replacement of tokens in the
+* `formatString` <String>: The string used to replace. Defaults to `"{{message}}"`
+* `replaceRule` <String>: The regex rule to use for replacement of tokens in the
   formatString. Defaults to `/{{(.*?)}}/g`
-* `separatorLength` <Number> : How long to print separators. Defaults to 60.
 * `isoDate` <Boolean> : Whether or not to convert `_timestamp` to ISO
-  date. Defaults to true.
-* `separatorType` <String> : The string to use for the separator.
-  Defaults to `"="`
-* `ansiColor` <Boolean> : Whether to use ANSI colors for output.
-  Defaults to `false`. This options depends on `colors`
-
-#### Options ####
-
-* **formatString**: A string that defines the format of the output. It is a
-  string with double curly braces denoting fields. For example:
-  `"[{{_timestamp}}] {{message}} (@{{_from}})"` would attempt to extract the
-  \_timestamp, message and \_from fields to create a string similar to this:
-  `"[124896126491.123] Testing the logger (@Client Application)"`
-  (defaults to `"{{message}}"`)
-* **ansiColor**: A boolean value, when `true` will output the string in ANSI
-  color depending on the severity level (defaults to `false`)
-
-### More Formatters? ###
-
-As with loggers, cobalt itself does not worry about these things. However,
-if you wish to make a formatter that is exchangable with Token, you just
-need to create an object that responds to the`format(logObject, optionsObject)`
-method:
+  date. Defaults to true. Otherwise it'll use the raw timestamp.
+
+#### Usage
+
+```javascript
+new Cologne.Formatter.Token({
+  formatString: '[{{_timestamp}}]{{_from}}: {{message}}'
+});
+```
+
+#### ANSI tokens
+
+If you want to add color to your logs, you can use the special \_ansi
+token. It has several options which you can call like `{{_ansi:red}}`
+and `{{_ansi:reset}}`. Here's a list of all the ansi stuff you can use:
+
+* `bold`: makes text bold
+* `italics`: makes text italics
+* `underline`: makes text underlined
+* `inverse`: inverts foreground and background
+* `strikethrough`: strikethrough text
+* `bold_off`, `italics_off`, `underline_off`, `inverse_off`, and
+  `strikethrough_off`: turn off the specified effect.
+* `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`,
+  and `default`: change the foreground color of your text.
+* `black_bg`, `red_bg`, `green_bg`, `yellow_bg`, `blue_bg`, `magenta_bg`,
+  `cyan_bg`, `white_bg`, and `default_bg`: change the background color of your
+  text.
+* `reset`: makes everything normal again.
+* `_level`: this is a special code that will set a color depending on
+  the level of the log: debug gets green, info and notice blue, warn is
+  yellow, and anything worse is red.
+
+### More Formatters?
+
+You can create your own formatters by creating an object that responds
+to the `#format()` method, knows how to handle cologne log objects and
+returns a string.
+
+Here's an example of a logger that surrounds a log with sparkles:
 
 ```javascript
-// A valid, very minimalistic formatter
 var simpleFormatter = {
-  format : function (logObject, options) {
-    if (options.showDate) {
-      return "[" + Date(logObject._timeStamp) + "] " + logObject.message
-    } else {
-      return logObject.message;
-    }
+  format : function (logObject) {
+    return '✨' + logObject.message + '✨';
   }
 }
 
-logger.addLogger(new Cobalt.Logger.JsConsole({
-  formatter: simpleFormatter,
-  formatterOpts : {
-    showDate : true
-  }
+logger.addLogger(new Cologne.Logger.Console({
+  formatter: simpleFormatter
 }));
 ```
 
-## The Cobalt Log Format ##
+## The Cologne Log Format
+
+The cologne log format is a JSON based log format, based on the cobalt
+log format, which is inturn based on Graylog's GELF. However, where GELF
+treats all internal fields without a prefix, and all user fields with a
+prefix we do it backwards so it's easier to extend the object with
+metadata from existing objects. Besides, we'll probably write the
+default keys automatically so you shouldn't have to do that extra work.
 
-The Cobalt Log (CoLog) format is a JSON based log format used with cobalt.
-It is partly inspired in Greylog's GELF format, but with very notorious
-differences. The CoLog requires a header with certain fields that allow
-cobalt and its pieces to handle it. All header fields are prefixed with
-an underscore. Other than those fields, you can put whatever you want in
-the object; It's up to the loggers to make sense of the structure and
-display it in a way that makes sense.
+You could try to build it on your own, but you can use `#buildLog()`
+to build it without logging.
 
-You can attempt to build this structure on your own, or let cobalt build it for 
-you. Any object you pass for logging will be converted. However, if you
-build it on your own you have two options: The first one is use buildLog
-to create a log object for "item" as if you had logged "item" or you can
-use extendLog that will create a dummy log object and extends it with
-whatever object you pass to it.
+### Fields
 
-### Required Fields ###
+* **\_timestamp** : A timestamp in miliseconds with fractions of a second
+  in the floating point area.
+* **\_cologneLog** <String> : This is how we know if the log is already
+  formatted and ready to go. This field is a string containing the
+  version of cologne log format it's using. It's `1.0.0` right now.
+* **\_from**: The sender of the log (Defaults to Generic Cologne Logger)
+* **\_level**: The level of the log (Defaults to 6)
+* **\_levelString**: The string corresponding to the log level (e.g. 7 ->
+  debug, 3 -> error, 0 -> emerg)
 
-* **_version** : The version of cobalt this is designed to work with
-* **_timestamp** : A timestamp in microseconds.
-* **_cobaltLog** [true] : Cobalt will check for the \_cobaltLog to decide if
-transformation will happen or not.
+### A word on Log Levels
 
-### Optional Fields ###
+The log levels in cologne correspond to the syslog levels, and the
+levelStrings correspond to the priority keywords:
 
-* **\_from**: The sender of the log (Defaults to Generic Cobalt Logger)
-* **\_level**: The level of the log (Defaults to 7)
-* **\_levelString**: The string corresponding to the log level (e.g. 7 ->
-  DEBUG, 3 -> ERROR, 0 -> CRIT)
-* **\_indentLevel**: The indent level of the log
-* **\_color**: The color of the log
-* **\_separator**: If true, indicates that this is a separator and holds no
-  valuable information.
+* `0 -> emerg`
+* `1 -> alert`
+* `2 -> crit`
+* `3 -> error`
+* `4 -> warning`
+* `5 -> notice`
+* `6 -> info`
+* `7 -> debug`
+
+This is useful when deciding how to log. You could even have a logger
+filter out unnecessary levels (eg. If you have a reporting logger that
+only reports error or worse.)
+
+## Further Improvements
+
+* Improve the API for buildLog
+* More loggers & formatters (will not be distributed in core cologne)
+* Improve tests
+
+[dependencies-shield]: https://david-dm.org/rbdr/cologne.svg
+[circle-ci-shield]: https://circleci.com/gh/rbdr/cologne.svg?style=shield
diff --git a/bower.json b/bower.json
deleted file mode 100644 (file)
index 8f33b28..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "name": "cobalt-log",
-  "version": "1.1.0",
-  "homepage": "https://github.com/rbdr/cobalt",
-  "authors": [
-    "Ben Beltran <ben@nsovocal.com>"
-  ],
-  "description": "Logger + multiplexer for JSON based logs",
-  "main": "lib/cobalt.js",
-  "keywords": [
-    "log"
-  ],
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "bower_components",
-    "test",
-    "tests"
-  ],
-  "dependencies": {
-    "neon" : "*"
-  }
-}
diff --git a/config/jsdoc.json b/config/jsdoc.json
new file mode 100644 (file)
index 0000000..1ad13c4
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "plugins": ["plugins/markdown"],
+  "opts": {
+    "destination": "doc",
+    "readme": "README.md",
+    "template": "node_modules/jsdoc-augmented-template",
+    "recurse": true
+  }
+}
diff --git a/lib/cobalt.js b/lib/cobalt.js
deleted file mode 100644 (file)
index b5dd94c..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-// Load up dependencies
-if (typeof require === 'function') {
-  require('neon');
-  var colors = require('colors');
-  var Microtime = require('microtime');
-}
-
-Cobalt = Module("Cobalt");
-Module(Cobalt, 'Logger')({});
-Module(Cobalt, 'Formatter')({});
-
-// Load up loggers + formatters
-if (typeof require === 'function') {
-  // Formatters
-  require('./formatters/token.js');
-  require('./formatters/simple.js');
-
-  // Loggers
-  require('./loggers/console.js');
-  require('./loggers/socket.js');
-  require('./loggers/file.js');
-}
-
-Cobalt.now = function () {
-  if (typeof performance !== 'undefined' && performance.timing) {
-    return performance.timing.navigationStart + performance.now();
-  }
-
-  if (typeof Microtime !== 'undefined') {
-    return Microtime.nowDouble() * 1000;
-  }
-
-  return Date.now();
-}
-
-// Stringify with circular dereference.
-Cobalt.stringify = function (object) {
-  var cache = [], stringified;
-  stringified = JSON.stringify(object, function (key, value) {
-    if (typeof value === 'object' && value !== null) {
-      if (cache.indexOf(value) !== -1) {
-        return "[Circular]";
-      }
-      cache.push(value);
-    }
-    return value;
-  });
-  cache = null;
-
-  return stringified;
-}
-
-Class(Cobalt, 'Console')({
-  prototype : {
-    from : "Generic Cobalt Logger",
-    version : "0.1.0",
-    currentIndent : 0,
-    indentSize : 2,
-    loggers : [],
-    separatorLength : 120,
-    currentColor : "black",
-
-    // Initialize instance of cobalt console
-    // and extend configuration.
-    init : function (config) {
-      var co = this,
-          property;
-
-      if (config) {
-        for (property in config) {
-          co[property] = config[property];
-        }
-      }
-    },
-
-    addLogger : function (logger) {
-      this.loggers.push(logger);
-    },
-
-    removeLogger : function (logger) {
-      var index;
-
-      index = this.loggers.indexOf(logger);
-      this.loggers.splice(index, 1);
-    },
-
-    // Builds a Cobalt Log Object
-    buildLog : function (item, level) {
-      var co = this, oldItem, logObject = {};
-
-      if (typeof item === "undefined" || item === null || !item._cobaltLog) {
-        logObject.message = item;
-        logObject._cobaltLog = true;
-        logObject._from = co.from;
-        logObject._level = level || 6;
-        logObject._levelString = co._levelString(logObject._level);
-        logObject._version = co.version;
-        logObject._timestamp = co.now();
-        logObject._indentLevel = co.currentIndent;
-        logObject._color = co.currentColor;
-        logObject._separator = false;
-        return logObject;
-      }
-
-      if (item._cobaltLog) {
-        item._level = level || item._level || 6;
-        item._levelString = co._levelString(item._level);
-      }
-
-      return item;
-    },
-
-    extendLog : function (extendingObject) {
-      var co = this, logObject,
-          property;
-
-      logObject = co.buildLog(undefined, 6);
-      extendingObject = extendingObject || {};
-
-      for (property in extendingObject) {
-        if (extendingObject.hasOwnProperty(property)) {
-          logObject[property] = extendingObject[property];
-        }
-      }
-
-      return logObject;
-    },
-
-    buildSeparator : function (type) {
-      var co = this;
-      return {
-        _cobaltLog : true,
-        _separator : true,
-        _version : co.version,
-        _timestamp : co.now(),
-        _separatorType : type,
-        _indentLevel : co.currentIndent,
-        _color : co.currentColor
-      }
-    },
-
-    _log : function (severity) {
-      var co = this,
-          logString,
-          logObjectArray = [],
-          i, j;
-
-      for (i = 1; i < arguments.length; i++) {
-        if (typeof arguments[i] === 'undefined') {
-          logObjectArray.push(co.buildLog("undefined", severity));
-        } else if (arguments[i] === null) {
-          logObjectArray.push(co.buildLog("null", severity));
-        } else {
-          logObjectArray.push(co.buildLog(arguments[i], severity));
-        }
-      }
-
-      for (j = 0; j < co.loggers.length; j++) {
-        co.loggers[j].log.apply(co.loggers[j], logObjectArray);
-      }
-    },
-
-    log : function () {
-      this._log.apply(this, [6].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    debug : function () {
-      this._log.apply(this, [7].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    info : function () {
-      this._log.apply(this, [6].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    notice : function () {
-      this._log.apply(this, [5].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    warn : function () {
-      this._log.apply(this, [4].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    error : function () {
-      this._log.apply(this, [3].concat(Array.prototype.slice.call(arguments)));
-    },
-
-    dir : function () {
-    },
-
-    time : function () {
-    },
-
-    timeEnd : function () {
-    },
-
-    groupCollapsed : function () {
-    },
-
-    groupEnd : function () {
-    },
-
-    separator : function (type) {
-      var co = this;
-
-      co._log(7, co.buildSeparator(type));
-    },
-
-    space : function (lines) {
-      var co = this,
-          i;
-
-      if (typeof lines === "undefined") {
-        lines = 1;
-      }
-
-      for (i = 0; i < lines; i++) {
-        co.log(' ');
-      }
-
-      return co;
-    },
-
-    indent : function (callback) {
-      var co = this;
-
-      if (typeof callback === "function") {
-        co.currentIndent = co.currentIndent + co.indentSize;
-        callback();
-        co.currentIndent = co.currentIndent - co.indentSize;
-      } else {
-        co.currentIndent = co.currentIndent + co.indentSize;
-      }
-
-      return co;
-    },
-
-    outdent : function (callback) {
-      var co = this;
-
-      if (typeof callback === "function") {
-        co.currentIndent = co.currentIndent - co.indentSize;
-        if (co.currentIndent < 0) {
-          co.currentIndent = 0;
-        }
-
-        callback();
-
-        co.currentIndent = co.currentIndent + co.indentSize;
-      } else {
-        co.currentIndent = co.currentIndent - co.indentSize;
-        if (co.currentIndent < 0) {
-          co.currentIndent = 0;
-        }
-      }
-
-      return co;
-    },
-
-    color : function (color, callback) {
-      var co = this,
-          oldColor = co.currentColor;
-
-      if (typeof callback === "function") {
-        co.currentColor = color;
-        callback();
-        co.currentColor = oldColor;
-      } else {
-        co.currentColor = color;
-      }
-
-      return co;
-    },
-
-    // Returns the current time in microseconds.
-    now : function () {
-      if (typeof performance !== 'undefined' && performance.timing) {
-        return performance.timing.navigationStart + performance.now();
-      }
-
-      if (typeof Microtime !== 'undefined') {
-        return Microtime.nowDouble() * 1000;
-      }
-
-      return Date.now();
-    },
-
-    _levelString : function (level) {
-      switch(level) {
-        case 0:
-          return "PANIC";
-          break;
-        case 1:
-          return "ALERT"
-          break;
-        case 2:
-          return "CRIT"
-          break;
-        case 3:
-          return "ERROR"
-          break;
-        case 4:
-          return "WARN"
-          break;
-        case 5:
-          return "NOTICE"
-          break;
-        case 6:
-          return "INFO"
-          break;
-        case 7:
-          return "DEBUG"
-          break;
-      }
-    }
-  }
-});
-
-if (Cobalt.Console.__objectSpy) {
-  Cobalt.Console.__objectSpy.destroy();
-}
diff --git a/lib/cologne.js b/lib/cologne.js
new file mode 100644 (file)
index 0000000..646fe79
--- /dev/null
@@ -0,0 +1,328 @@
+'use strict';
+
+let LogUtilities = require('./cologne/log_utilities');
+
+/** TYPE DEFINITIONS **/
+
+/**
+ * Main interface for Cologne Loggers
+ *
+ * @memberof Cologne
+ * @interface ILogger
+ */
+
+/**
+ * Receives any number of cologne log objects and logs them.
+ *
+ * @memberof Cologne.ILogger
+ * @function
+ * @name log
+ * @returns {undefined}
+ */
+
+/**
+ * Main interface for Cologne Formatters
+ *
+ * @memberof Cologne
+ * @interface IFormatter
+ */
+
+/**
+ * Receives a cologne log object and returns a formatted string.
+ *
+ * @memberof Cologne.IFormatter
+ * @function
+ * @name format
+ * @param {Cologne.tCologneLog} logObject the log to be formatted
+ * @returns {string} the formatted log
+ */
+
+/**
+ * The main cologne log format.
+ *
+ * @memberof Cologne
+ * @typedef {object} tCologneLog
+ * @property {Number} _timestamp the timestamp in miliseconds with decimal
+ * numbers representing fractions of miliseconds
+ * @property {String} _cologneLog main identifier, encodes the version of the
+ * cologne log format being used.
+ * @property {String} _from the origin of the log message.
+ * @property {String} _level the severity level of the log, uses syslog
+ * priorities.
+ * @property {String} _levelString the severity level keyword of the log,
+ * uses syslog priority keywords.
+ */
+
+/**
+ * Cologne is a logger multiplexer that works mainly with a JSON format. It
+ * can be instantiated with several loggers, or they can be changed after
+ * the fact.
+ *
+ * ## Usage
+ *
+ * ```
+ * require('cologne');
+ *
+ * let co = new Cologne({
+ *   from: "Special Worker Logger",
+ *   loggers: [
+ *     new Cologne.Logger.Console({
+ *       formatter: new Cologne.Formatter.Token({
+ *         formatString: '[{{_timestamp}}]{{_from}}: {{message}}'
+ *       })
+ *     })
+ *   ]
+ * });
+ * ```
+ *
+ * @class Cologne
+ */
+let Cologne = class Cologne {
+
+  constructor (config) {
+
+    /**
+     * The name of this logger, useful to distinguish between different
+     * loggers.
+     *
+     * @name from
+     * @instance
+     * @memberof Cologne
+     * @type String
+     * @default 'Generic Cologne Logger
+     */
+    this.from = 'Generic Cologne Logger';
+
+    /**
+     * The array containing all the loggers it will call to.
+     *
+     * @name loggers
+     * @instance
+     * @memberof Cologne
+     * @type Cologne.ILogger[]
+     * @default []
+     */
+    this.loggers = [];
+
+    Object.assign(this, config || {});
+  }
+
+  /**
+   * Adds a logger to the current instance.
+   *
+   * @function addLogger
+   * @instance
+   * @memberof Cologne
+   * @param {Cologne.ILogger} logger the logger to add
+   * @return {undefined}
+   */
+  addLogger (logger) {
+    this.loggers.push(logger);
+  }
+
+  /**
+   * Removes a logger from the current instance.
+   *
+   * @function removeLogger
+   * @instance
+   * @memberof Cologne
+   * @param {Cologne.ILogger} logger the logger to remove
+   * @return {Cologne.ILogger[]} the removed log, inside an array.
+   */
+  removeLogger (logger) {
+    let index;
+
+    index = this.loggers.indexOf(logger);
+    if (index >= 0) {
+      this.loggers.splice(index, 1);
+    }
+  }
+
+  /**
+   * Given an item, it builds a cologne log object. this is done
+   * automatically by the logger, though this is useful if you need
+   * to attach metadata before logging.
+   *
+   * @function buildLog
+   * @instance
+   * @memberof Cologne
+   * @param {*} item The item to log
+   * @return {Cologne.tCologneLog} a cologne log object
+   */
+  buildLog (item, level, meta) {
+    let logObject;
+
+    logObject = {};
+
+    if (typeof item === 'undefined' || item === null || !item._cologneLog) {
+      logObject.message = item;
+      logObject._cologneLog = this.constructor._version;
+      logObject._from = this.from;
+      logObject._level = level || 6;
+      logObject._levelString = this._levelString(logObject._level);
+      logObject._timestamp = LogUtilities.now();
+
+      if (meta && typeof meta === 'object') {
+        Object.assign(logObject, meta);
+      }
+
+      return logObject;
+    }
+
+    if (item._cologneLog) {
+      item._level = level || item._level || 6;
+      item._levelString = this._levelString(item._level);
+    }
+
+    return item;
+  }
+
+  /**
+   * Default log function. Sends arguments to loggers. If not specified in log
+   * object, it will set the severity to 6 - INFO.
+   *
+   * @function log
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  log () {
+    this._log.apply(this, [null].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  /**
+   * Logs with debug level
+   *
+   * @function debug
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  debug () {
+    this._log.apply(this, [7].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  /**
+   * Logs with info level
+   *
+   * @function info
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  info () {
+    this._log.apply(this, [6].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  /**
+   * Logs with notice level
+   *
+   * @function notice
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  notice () {
+    this._log.apply(this, [5].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  /**
+   * Logs with warn level
+   *
+   * @function warn
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  warn () {
+    this._log.apply(this, [4].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  /**
+   * Logs with error level
+   *
+   * @function error
+   * @instance
+   * @memberof Cologne
+   * @return {undefined}
+   */
+  error () {
+    this._log.apply(this, [3].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  // Private method that builds all the logs and sends them to the loggers.
+  _log (severity) {
+    let remainingArguments, logObjectArray, log, logger;
+
+    remainingArguments = Array.prototype.slice.call(arguments, 1);
+    logObjectArray = [];
+
+    for (log of remainingArguments) {
+      if (typeof log === 'undefined') {
+        logObjectArray.push(this.buildLog('undefined', severity));
+        continue;
+      }
+
+      if (log === null) {
+        logObjectArray.push(this.buildLog('null', severity));
+        continue;
+      }
+
+      logObjectArray.push(this.buildLog(log, severity));
+    }
+
+    for (logger of this.loggers) {
+      logger.log.apply(logger, logObjectArray);
+    }
+  }
+
+  // Private utility method that will return the string for any given
+  // numerical severity level
+  _levelString (level) {
+    switch(level) {
+    case 0:
+      return 'emerg';
+    case 1:
+      return 'alert';
+    case 2:
+      return 'crit';
+    case 3:
+      return 'error';
+    case 4:
+      return 'warning';
+    case 5:
+      return 'notice';
+    case 6:
+      return 'info';
+    case 7:
+      return 'debug';
+    }
+  }
+};
+
+// Version of the Cologne Log Format used.
+Cologne._version = '1.0.0';
+
+/**
+ * Namespace that includes the built-in formatters.
+ *
+ * @namespace Formatter
+ * @memberof Cologne
+ */
+Cologne.Formatter = {};
+Cologne.Formatter.Simple = require('./cologne/formatter/simple');
+Cologne.Formatter.Token = require('./cologne/formatter/token');
+
+/**
+ * Namespace that includes the built-in loggers.
+ *
+ * @namespace Logger
+ * @memberof Cologne
+ */
+Cologne.Logger = {};
+Cologne.Logger.Console = require('./cologne/logger/console');
+Cologne.Logger.File = require('./cologne/logger/file');
+
+Cologne.LogUtilities = require('./cologne/log_utilities');
+
+module.exports = Cologne;
diff --git a/lib/cologne/formatter/simple.js b/lib/cologne/formatter/simple.js
new file mode 100644 (file)
index 0000000..48821fc
--- /dev/null
@@ -0,0 +1,67 @@
+'use strict';
+
+let LogUtilities = require('../log_utilities');
+
+/**
+ * Simple formatter. Outputs a predefined format:
+ * `[{{_timestamp}}][{{_levelString}}] {{_from}}: {{message}}`;
+ *
+ * @memberof Cologne.Formatter
+ * @implements Cologne.IFormatter
+ * @class Simple
+ */
+let SimpleFormatter = class SimpleFormatter {
+
+  constructor (config) {
+
+    /**
+     * Flag that tells us whether or not to use ANSI color. Defaults to
+     * false.
+     *
+     * @name colorize
+     * @instance
+     * @memberof Cologne.Formatter.Simple
+     * @type Boolean
+     * @default false
+     */
+    this.colorize = false;
+
+    Object.assign(this, config || {});
+  }
+
+  /**
+   * Main entry point, it will read the incoming log object and convert
+   * it to the output string.
+   *
+   * @function format
+   * @instance
+   * @memberof Cologne.Formatter.Simple
+   * @param {Cologne.tCologneLog} logObjet the log to format
+   * @return {String} the formatted object
+   */
+  format (logObject) {
+    let date, levelString;
+
+    date = new Date(logObject._timestamp);
+    date = date.toISOString();
+    levelString = this._colorize(logObject._levelString, logObject._level);
+
+    return `[${date}][${levelString}] ${logObject._from}: ${logObject.message}`;
+  }
+
+  _colorize (levelString, level) {
+    let escapeCode, color, reset;
+
+    if (!this.colorize) {
+      return levelString;
+    }
+
+    escapeCode = String.fromCharCode(27);
+    color = escapeCode + LogUtilities.getAnsiCode(LogUtilities.getLevelAnsi(level));
+    reset = escapeCode + LogUtilities.getAnsiCode('reset');
+
+    return color + levelString + reset;
+  }
+};
+
+module.exports = SimpleFormatter;
diff --git a/lib/cologne/formatter/token.js b/lib/cologne/formatter/token.js
new file mode 100644 (file)
index 0000000..bb905ca
--- /dev/null
@@ -0,0 +1,104 @@
+'use strict';
+
+let LogUtilities = require('../log_utilities');
+
+/**
+ * Token formatter. Given a format string it will attempt to output
+ * a message.
+ *
+ * @memberof Cologne.Formatter
+ * @implements Cologne.IFormatter
+ * @class Token
+ */
+let TokenFormatter = class TokenFormatter {
+  constructor (config) {
+
+    /**
+     * The string to use as a template string. By default, any property
+     * inside double curly braces `{{likeThis}}` will be extracted from
+     * the object and replaced. If the object does not contain the
+     * property, it will leave it.
+     *
+     * @name formatString
+     * @instance
+     * @memberof Cologne.Formatter.Token
+     * @type String
+     * @default '{{message}}'
+     */
+    this.formatString = '{{message}}';
+
+    /**
+     * The regex rule to use to match the tokens.
+     *
+     * @name replaceRule
+     * @instance
+     * @memberof Cologne.Formatter.Token
+     * @type RegExp
+     * @default /{{(.*)}}/g
+     */
+    this.replaceRule = /{{(.*?)}}/g;
+
+    /**
+     * Flag that specifies whether or not to use an isoDate when using
+     * `_timestamp`. If false it will output the raw timestamp.
+     *
+     * @name isoDate
+     * @instance
+     * @memberof Cologne.Formatter.Token
+     * @type Boolean
+     * @default true
+     */
+    this.isoDate = true;
+
+    this._ansiRe = /_ansi:.+/;
+
+    Object.assign(this, config || {});
+  }
+
+  /**
+   * Main entry point, it will read the incoming log object and convert
+   * all the tokens to their corresponding representation, finally
+   * returning the string.
+   *
+   * @function format
+   * @instance
+   * @memberof Cologne.Formatter.Token
+   * @param {Cologne.tCologneLog} logObjet the log to format
+   * @return {String} the formatted object
+   */
+  format (logObject) {
+    let resultString, escapeCode;
+
+    escapeCode = String.fromCharCode(27);
+
+    resultString = this.formatString.replace(this.replaceRule, function (match, token) {
+      let date, ansiType;
+
+      if (token === '_timestamp' && this.isoDate) {
+        date = new Date(logObject._timestamp);
+        return date.toISOString();
+      }
+
+      if (token.match(this._ansiRe)) {
+        ansiType = token.split(':')[1];
+
+        // Smartish coloring
+        if (ansiType === '_level') {
+          return escapeCode + LogUtilities.getAnsiCode(LogUtilities.getLevelAnsi(logObject._level));
+        }
+
+        return escapeCode + LogUtilities.getAnsiCode(ansiType);
+      }
+
+      if (!logObject.hasOwnProperty(token)) {
+        return match;
+      }
+
+      return logObject[token];
+    }.bind(this));
+
+    return resultString;
+  }
+};
+
+module.exports = TokenFormatter;
diff --git a/lib/cologne/log_utilities.js b/lib/cologne/log_utilities.js
new file mode 100644 (file)
index 0000000..fc15a8a
--- /dev/null
@@ -0,0 +1,155 @@
+'use strict';
+
+let microtime = require('microtime');
+
+/**
+ * Container object for utilities used by loggers.
+ *
+ * @memberof Cologne
+ * @class LogUtilities
+ */
+let LogUtilities = {
+
+  /**
+   * Returns the current timestamp in miliseconds as a floating point that
+   * includes fractions (ie. microseconds)
+   *
+   * @function now
+   * @memberof Cologne.LogUtilities
+   * @return {Number} current time in miliseconds, including fractions.
+   */
+  now: function now() {
+    return microtime.nowDouble() * 1000;
+  },
+
+  /**
+   * Stringifies objects, avoiding circular references.
+   *
+   * @function stringify
+   * @memberof Cologne.LogUtilities
+   * @param {Object} object the object to stringify
+   * @return {String} the stringified object
+   */
+  stringify: function stringify(object) {
+    let cache;
+
+    cache = [];
+
+    return JSON.stringify(object, function (key, value) {
+      if (typeof value === 'object' && value !== null) {
+        if (cache.indexOf(value) !== -1) {
+          return this._circularString;
+        }
+
+        cache.push(value);
+      }
+
+      return value;
+    }.bind(this));
+  },
+
+  /**
+   * Given an ansi keyword, it will return the appropriate code.
+   *
+   * @function getAnsiCode
+   * @memberof Cologne.LogUtilities
+   * @param {String} ansiString the name of the desired code
+   * @return {String} the ansi code
+   */
+  getAnsiCode: function getAnsiCode(ansiString) {
+    switch(ansiString) {
+    case 'bold':
+      return '[1m';
+    case 'italics':
+      return '[3m';
+    case 'underline':
+      return '[4m';
+    case 'inverse':
+      return '[7m';
+    case 'strikethrough':
+      return '[9m';
+    case 'bold_off':
+      return '[22m';
+    case 'italics_off':
+      return '[23m';
+    case 'underline_off':
+      return '[24m';
+    case 'inverse_off':
+      return '[27m';
+    case 'strikethrough_off':
+      return '[29m';
+    case 'black':
+      return '[30m';
+    case 'red':
+      return '[31m';
+    case 'green':
+      return '[32m';
+    case 'yellow':
+      return '[33m';
+    case 'blue':
+      return '[34m';
+    case 'magenta':
+      return '[35m';
+    case 'cyan':
+      return '[36m';
+    case 'white':
+      return '[37m';
+    case 'default':
+      return '[39m';
+    case 'black_bg':
+      return '[40m';
+    case 'red_bg':
+      return '[41m';
+    case 'green_bg':
+      return '[42m';
+    case 'yellow_bg':
+      return '[43m';
+    case 'blue_bg':
+      return '[44m';
+    case 'magenta_bg':
+      return '[45m';
+    case 'cyan_bg':
+      return '[46m';
+    case 'white_bg':
+      return '[47m';
+    case 'default_bg':
+      return '[49m';
+    case 'reset': // for informative purpouses
+    default:
+      return '[0m';
+    }
+  },
+
+  /**
+   * Given a level, it will return the appropriate ansi keyword related
+   * to it.
+   *
+   * @function getLevelAnsi
+   * @memberof Cologne.LogUtilities
+   * @param {number} level the level of the log
+   * @return {String} the ansi keyword
+   */
+  getLevelAnsi: function getLevelAnsi(level) {
+    switch(level) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      return 'red';
+    case 4:
+      return 'yellow';
+    case 5:
+    case 6:
+      return 'blue';
+    case 7:
+      return 'green';
+    default:
+      return 'default';
+    }
+  }
+};
+
+// String used as default circular reference.
+LogUtilities._circularString = '[Circular]';
+
+module.exports = LogUtilities;
diff --git a/lib/cologne/logger/console.js b/lib/cologne/logger/console.js
new file mode 100644 (file)
index 0000000..3a73e42
--- /dev/null
@@ -0,0 +1,95 @@
+'use strict';
+
+let LogUtilities = require('../log_utilities');
+
+/**
+ * Logger for the javascript console.
+ *
+ * @memberof Cologne.Logger
+ * @implements Cologne.ILogger
+ * @class Console
+ */
+let ConsoleLogger = class ConsoleLogger {
+  constructor (config) {
+
+    /**
+     * The console it will write to, can be any object that looks
+     * and acts like a console, including other cologne objects.
+     *
+     * @name console
+     * @instance
+     * @memberof Cologne.Logger.Console
+     * @type Object
+     * @default global.console
+     */
+    this.console = console;
+
+    /**
+     * The formatter it will use to output the log. If not present it
+     * will output raw JSON
+     *
+     * @name formatter
+     * @instance
+     * @memberof Cologne.Logger.Console
+     * @type Cologne.IFormatter
+     * @default null
+     */
+    this.formatter = null;
+
+    Object.assign(this, config || {});
+  }
+
+
+  /**
+   * Main entry point, for each incoming argument it will attempt to
+   * format and send to the console.
+   *
+   * @function log
+   * @instance
+   * @memberof Cologne.Logger.Console
+   * @return {undefined}
+   */
+  log () {
+    let logObject, messages, severity;
+
+    messages = [];
+
+    for (logObject of arguments) {
+      messages.push(this._format(logObject));
+
+      if (!severity) {
+        severity = logObject._level;
+      }
+    }
+
+    switch(severity) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      this.console.error.apply(this.console, messages);
+      break;
+    case 4:
+      this.console.warn.apply(this.console, messages);
+      break;
+    case 5:
+    case 6:
+      this.console.info.apply(this.console, messages);
+      break;
+    case 7:
+    default:
+      this.console.log.apply(this.console, messages);
+      break;
+    }
+  }
+
+  _format (logObject) {
+    if (this.formatter) {
+      return this.formatter.format(logObject);
+    }
+
+    return LogUtilities.stringify(logObject);
+  }
+};
+
+module.exports = ConsoleLogger;
diff --git a/lib/cologne/logger/file.js b/lib/cologne/logger/file.js
new file mode 100644 (file)
index 0000000..e9a56e7
--- /dev/null
@@ -0,0 +1,71 @@
+'use strict';
+
+let fs = require('fs');
+
+let LogUtilities = require('../log_utilities');
+
+/**
+ * Logger for files.
+ *
+ * @memberof Cologne.Logger
+ * @implements Cologne.ILogger
+ * @class File
+ */
+let FileLogger = class FileLogger {
+  constructor (config) {
+
+    /**
+     * Path to the file it will write to, must be readable.
+     *
+     * @name file
+     * @instance
+     * @memberof Cologne.Logger.File
+     * @type string
+     * @default null
+     */
+    this.file = null;
+
+    /**
+     * The formatter it will use to output the log. If not present it
+     * will output raw JSON
+     *
+     * @name formatter
+     * @instance
+     * @memberof Cologne.Logger.File
+     * @type Cologne.IFormatter
+     * @default null
+     */
+    this.formatter = null;
+
+    Object.assign(this, config || {});
+
+    this._stream = fs.createWriteStream(this.file, {flags: 'a'});
+  }
+
+  /**
+   * Main entry point, for each incoming argument it will attempt to
+   * format and send to the stream to be written.
+   *
+   * @function log
+   * @instance
+   * @memberof Cologne.Logger.File
+   * @return {undefined}
+   */
+  log () {
+    let logObject;
+
+    for (logObject of arguments) {
+      this._stream.write(this.format(logObject) + '\n');
+    }
+  }
+
+  format (logObject) {
+    if (this.formatter) {
+      return this.formatter.format(logObject);
+    }
+
+    return LogUtilities.stringify(logObject);
+  }
+};
+
+module.exports = FileLogger;
diff --git a/lib/ext/socket_helper.js b/lib/ext/socket_helper.js
deleted file mode 100644 (file)
index 50fe5bc..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-var bindEvents = function (socket, logger) {
-    socket.on('log', function (logArgs) {
-      logger.log.apply(logger, logArgs);
-    });
-}
-
-exports.bindEvents = bindEvents;
diff --git a/lib/formatters/ansi.js b/lib/formatters/ansi.js
deleted file mode 100644 (file)
index 806869a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-if (typeof require === 'function') {
-    require('colors');
-}
-
-Module(Cobalt.Formatter, 'Ansi')({
-  format : function (logObject, opts){
-    var indent,
-        message;
-
-    indent = Array(logObject._indentLevel + 1).join(' ');
-
-    message = indent + logObject.message;
-
-    switch(logObject._level) {
-      case 0:
-      case 1:
-      case 2:
-      case 3:
-        return message.red;
-      case 4:
-        return message.yellow;
-      case 5:
-      case 6:
-        return message.blue;
-      default:
-        return message;
-    }
-  }
-});
diff --git a/lib/formatters/simple.js b/lib/formatters/simple.js
deleted file mode 100644 (file)
index 9eb6bbe..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Module(Cobalt.Formatter, 'Simple')({
-    format : function (logObject, opts){
-        var indent, date;
-
-        indent = Array(logObject._indentLevel + 1).join(' ');
-
-        date = new Date(logObject._timestamp);
-
-        return indent + '[' + date.toISOString() + '][' + logObject._levelString + '] ' + logObject._from + ' : ' + logObject.message;
-    }
-});
diff --git a/lib/formatters/token.js b/lib/formatters/token.js
deleted file mode 100644 (file)
index f3afba2..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-Module(Cobalt.Formatter, 'Token')({
-  formatString : "{{message}}",
-  replaceRule : /{{(.*?)}}/g,
-  separatorLength : 60,
-  isoDate : true,
-  separatorType : "-",
-  format : function (logObject, opts){
-    var indent, indentSize,
-        separatorLength, separatorType,
-        output, property;
-    indentSize = logObject._indentLevel || 0;
-
-    // Extend opts
-    if (opts) {
-      for (property in opts) {
-        if (opts.hasOwnProperty(property)) {
-          this[property] = opts[property];
-        }
-      }
-    }
-
-    indent = Array(indentSize + 1).join(' ');
-
-    if (logObject._separator) {
-      separatorLength = logObject._separatorLength || this.separatorLength;
-      separatorType = logObject._separatorType || this.separatorType;
-      output = indent + Array(separatorLength - indentSize + 1).join(separatorType);
-    } else {
-      output = indent + this.parseFormatString(logObject, this.formatString);
-    }
-
-    if (this.ansiColor) {
-      output = this.colorize(logObject._level, output);
-    }
-
-    return output;
-  },
-
-  parseFormatString : function (logObject, formatString) {
-    var resultString = '';
-    if (typeof formatString === 'undefined') {
-      formatString = this.formatString;
-    }
-
-    resultString = formatString.replace(this.replaceRule, function(match, paren){
-      var date;
-      if (paren === "_timestamp" && this.isoDate) {
-        date = new Date(logObject[paren]);
-        return date.toISOString();
-      }
-      return logObject[paren] || "-";
-    }.bind(this));
-
-    return resultString;
-  },
-
-  colorize : function (level, message) {
-    switch(level) {
-      case 0:
-      case 1:
-      case 2:
-      case 3:
-        return message.red;
-      case 4:
-        return message.yellow;
-      case 5:
-      case 6:
-        return message.blue;
-      default:
-        return message;
-    }
-  }
-});
diff --git a/lib/loggers/console.js b/lib/loggers/console.js
deleted file mode 100644 (file)
index c4031eb..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-Class(Cobalt.Logger, 'JsConsole')({
-  prototype : {
-    console : null,
-    formatterOpts : {},
-
-    init : function (config) {
-      var logger = this,
-          property;
-
-      if (config) {
-        for (property in config) {
-          logger[property] = config[property];
-        }
-      }
-
-      if (!logger.console) {
-        logger.console = console;
-      }
-    },
-
-    log : function () {
-      var i, message = [], severity;
-
-      for (i = 0; i < arguments.length; i++) {
-        // We're not formatting objects for now.
-
-        if (!arguments[i].__skipConsole && !arguments[i].message.__skipConsole) {
-          if (typeof arguments[i].message === 'object') {
-            message.push(arguments[i].message);
-          } else {
-            message.push(this.format(arguments[i]));
-          }
-          if (!severity) {
-            severity = arguments[i]._level
-          }
-        }
-      }
-
-      switch (severity){
-        case 0:
-        case 1:
-        case 2:
-        case 3:
-          this.console.error.apply(this.console, message);
-          break;
-        case 4:
-          this.console.warn.apply(this.console, message);
-          break;
-        case 5:
-        case 6:
-          this.console.info.apply(this.console, message);
-          break;
-        case 7:
-        default:
-          this.console.log.apply(this.console, message);
-          break;
-      }
-    },
-
-    format : function (logObject) {
-      // Usually what you want to do here is format. Preferably using
-      // someone inside Cobalt.Formatter
-      if (this.formatter) {
-        return this.formatter.format(logObject, this.formatterOpts);
-      }
-
-      return logObject.message;
-    }
-  }
-});
diff --git a/lib/loggers/file.js b/lib/loggers/file.js
deleted file mode 100644 (file)
index 0d60acd..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-var fs = require('fs');
-
-Class(Cobalt.Logger, 'File')({
-  prototype : {
-    file : null,
-    formatterOpts : {},
-
-    init : function (config) {
-      if (config) {
-        for (property in config) {
-          this[property] = config[property];
-        }
-      }
-
-      this._stream = fs.createWriteStream(this.file, {flags: 'a'});
-    },
-
-    log : function () {
-      var i, message = [], severity;
-
-      for (i = 0; i < arguments.length; i++) {
-        // We're not formatting objects for now.
-
-        if (!arguments[i].__skipConsole && !arguments[i].message.__skipConsole) {
-          message.push(this.format(arguments[i]));
-          if (!severity) {
-            severity = arguments[i]._level
-          }
-        }
-      }
-
-      for (i = 0; i < message.length; i++) {
-        this._stream.write(message[i] + '\n');
-      }
-    },
-
-    format : function (logObject) {
-      if (this.formatter) {
-        if (typeof logObject.message === 'object') {
-          return logObject.message;
-        }
-        return this.formatter.format(logObject, this.formatterOpts);
-      }
-
-      return Cobalt.stringify(logObject);
-    }
-  }
-});
diff --git a/lib/loggers/socket.js b/lib/loggers/socket.js
deleted file mode 100644 (file)
index 7066274..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-if (typeof require === 'function') {
-  var ioClient = require('socket.io-client');
-}
-
-Class(Cobalt.Logger, 'Socket')({
-  prototype : {
-    serverUrl : '/',
-
-    init : function (config) {
-      var logger = this;
-
-      if (config) {
-        for (property in config) {
-          logger[property] = config[property];
-        }
-      }
-
-      if (!logger.socketIo) {
-        logger.socketIo = ioClient;
-      }
-
-      logger._socket = logger.socketIo.connect(logger.serverUrl);
-    },
-
-    log : function () {
-      var i, messageArray = [];
-
-      for (i = 0; i < arguments.length; i++) {
-        messageArray.push(arguments[i]);
-      }
-
-      if (this._socket) {
-        this._socket.emit('log', messageArray);
-      }
-    }
-  }
-});
index f5ea0a9ad8d7775f18f2fbd2ead555546ffc01e2..6f7b73dce9090c601b63fbf3a0d136fc69b3e31b 100644 (file)
@@ -1,35 +1,29 @@
 {
-  "name": "cobalt-log",
-  "description": "Logger + multiplexer for JSON based logs, based on Cobalt ruby gem by ktlacaelel (http://rubygems.org/gems/cobalt)",
+  "name": "cologne",
+  "description": "Logger + multiplexer for JSON based logs",
   "author": {
     "name": "Ben Beltran",
     "email": "ben@nsovocal.com",
-    "url": "http://nsovocal.com"
+    "url": "http://unlimited.pizza"
   },
   "repository": {
     "type": "git",
-    "url": "https://github.com/rbdr/cobalt.git"
+    "url": "https://github.com/rbdr/cologne.git"
   },
-  "contributors": [
-    {
-      "name": "Kazuyoshi Tlacaelel",
-      "email": "kazu.dev@gmail.com",
-      "url": "http://github.com/ktlacaelel"
-    }
-  ],
-  "version": "1.1.3",
+  "version": "1.1.0",
   "dependencies": {
-    "colors": "^1.0.3",
-    "emailjs": "^0.3.12",
-    "microtime": "^1.2.0",
-    "neon": "^2.0.0",
-    "socket.io-client": "^1.2.1"
+    "microtime": "2.0.x"
   },
   "devDependencies": {
-    "tellurium": "2.0.x"
+    "tap": "5.1.x",
+    "jsdoc-augmented-template": "rbdr/jsdoc-augmented-template"
   },
   "engines": {
-    "node": ">= 0.8.0"
+    "node": ">= 4.0.0"
   },
-  "main": "./lib/cobalt.js"
+  "scripts": {
+    "test": "node_modules/tap/bin/run.js test/*.js test/**/*.js test/**/**/*.js",
+    "document": "jsdoc -c ./config/jsdoc.json lib"
+  },
+  "main": "./lib/cologne.js"
 }
diff --git a/test/basic.js b/test/basic.js
deleted file mode 100644 (file)
index 90aadd6..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-if (typeof require === "function") {
-    require("cobalt-log");
-}
-
-var co = new Cobalt.Console({
-    loggers : [
-        new Cobalt.Logger.JsConsole({
-            formatter     : Cobalt.Formatter.Token,
-            formatterOpts :  {
-              formatString : "[{{_level}}] {{message}} {{customParam}}"
-            }
-        })
-    ]
-})
-
-// TODO: Do this whole thing with tellurium.
-
-co.log("Log - Normal");
-co.debug("Warn - Normal");
-co.info("Info - Normal");
-co.notice("Notice - Normal");
-co.warn("Warn - Normal");
-co.error("Error - Normal");
-
-var logObject = co.extendLog({
-    message : "Extended Log Object",
-    customParam : "<3"
-});
-
-co.log(logObject);
-co.debug(logObject);
-co.info(logObject);
-co.notice(logObject);
-co.warn(logObject);
-co.error(logObject);
diff --git a/test/browser.html b/test/browser.html
deleted file mode 100644 (file)
index ec58b6f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Tellurium Test Runner</title>
-
-    <!-- load neon dependency. These are node modules, I'm not sure this is a good way to load it -->
-    <script src="../node_modules/neon/neon.js" type="text/javascript" charset="utf-8"></script>
-
-    <!-- include cobalt -->
-    <script src="../node_modules/cobalt-log/lib/cobalt.js" type="text/javascript" charset="utf-8"></script>
-    <script src="../node_modules/cobalt-log/lib/loggers/console.js" type="text/javascript" charset="utf-8"></script>
-    <script src="../node_modules/cobalt-log/lib/formatters/token.js" type="text/javascript" charset="utf-8"></script>
-
-    <!-- include test files here... -->
-    <script type="text/javascript" src="basic.js"></script>
-
-    </head>
-  <body>
-  </body>
-</html>
diff --git a/test/cologne.js b/test/cologne.js
new file mode 100644 (file)
index 0000000..f5a6c6e
--- /dev/null
@@ -0,0 +1,212 @@
+'use strict';
+
+let tap = require('tap');
+
+let Cologne = require('../lib/cologne');
+
+let dummyLogger = {
+  values : null,
+  log : function () {
+    let logObject;
+
+    this.values = [];
+
+    for (logObject of arguments) {
+      this.values.push(logObject);
+    }
+  }
+};
+
+// Prepare the test
+let dummyLoggerA, dummyLoggerB, dummyLoggerC,
+  co, params, builtLog, meta,
+  valueCheck, levelCheck;
+
+tap.plan(18);
+
+dummyLoggerA = Object.assign({}, dummyLogger);
+dummyLoggerB = Object.assign({}, dummyLogger);
+dummyLoggerC = Object.assign({}, dummyLogger);
+
+co = new Cologne({
+  loggers : [
+    dummyLoggerA,
+    dummyLoggerB
+  ]
+});
+
+meta = {
+  rainbows: true
+};
+
+params = ['example1', null, undefined, 1, {example: true}];
+
+/**
+ * TEST: #log()
+ */
+co.log.apply(co, params);
+
+// Calculate values
+valueCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (typeof current._cologneLog === 'string') {
+    previous++;
+  }
+  return previous;
+}, 0);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 6) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+// Now check the values
+
+tap.equal(dummyLoggerA.values.length, params.length,
+          '#log() should send every argument to the loggers');
+
+tap.similar(dummyLoggerA.values, dummyLoggerB.values,
+            '#log() should send the same arguments to all the loggers');
+
+tap.equal(valueCheck, params.length,
+          '#log() should send all objects in cologne log format');
+
+tap.equal(levelCheck, params.length,
+          '#log() should default to level 6');
+
+/**
+ * TEST: #debug()
+ */
+co.debug.apply(co, params);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 7) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, params.length,
+          '#debug() should set to level 7');
+
+/**
+ * TEST: #info()
+ */
+co.info.apply(co, params);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 6) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, params.length,
+          '#info() should set to level 6');
+
+/**
+ * TEST: #notice()
+ */
+co.notice.apply(co, params);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 5) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, params.length,
+          '#notice() should set to level 5');
+
+/**
+ * TEST: #warn()
+ */
+co.warn.apply(co, params);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 4) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, params.length,
+          '#warn() should set to level 4');
+
+/**
+ * TEST: #error()
+ */
+co.error.apply(co, params);
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 3) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, params.length,
+          '#error() should set to level 3');
+
+/**
+ * TEST: #buildLog()
+ */
+builtLog = co.buildLog('example');
+
+// With the default level
+tap.equal(typeof builtLog._cologneLog, 'string',
+          '#buildLog() should return a cologne log');
+tap.equal(builtLog._level, 6,
+          '#buildLog() should default to level 6');
+
+// Now with a specific value
+builtLog = co.buildLog('example', 1);
+
+tap.equal(builtLog._level, 1,
+          '#buildLog() should use the specified level');
+
+// Now with meta
+builtLog = co.buildLog('example', 1, meta);
+
+tap.equal(builtLog.rainbows, true,
+          '#buildLog() should extend the object with meta if available');
+
+/**
+ * TEST: #log() with builtLog.
+ */
+co.log(builtLog);
+
+levelCheck = dummyLoggerA.values.reduce(function (previous, current) {
+  if (current._level === 1) {
+    previous++;
+  }
+  return previous;
+}, 0);
+
+tap.equal(levelCheck, 1,
+          '#log() calls using a pre-built cologne log should maintain the log level');
+
+
+/**
+ * TEST: #removeLogger()
+ */
+co.removeLogger(dummyLoggerC);
+
+tap.equal(co.loggers.length, 2,
+          '#removeLogger() should do nothing if it can\'t find a logger');
+
+co.log.apply(co, params);
+co.removeLogger(dummyLoggerB);
+co.log(1);
+
+tap.equal(co.loggers.length, 1,
+          '#removeLogger() should remove a logger');
+
+tap.notEqual(dummyLoggerB.values.length, dummyLoggerA.values.length,
+            '#removeLogger() should no longer affect removed logs');
+
+/**
+ * TEST: #addLogger()
+ */
+
+co.addLogger(dummyLoggerC);
+co.log.apply(co, params);
+
+tap.equal(dummyLoggerC.values.length, params.length,
+            '#addLogger() should add loggers after instance is live');
diff --git a/test/cologne/formatter/simple.js b/test/cologne/formatter/simple.js
new file mode 100644 (file)
index 0000000..8642541
--- /dev/null
@@ -0,0 +1,73 @@
+'use strict';
+
+let tap = require('tap');
+
+let SimpleFormatter = require('../../../lib/cologne/formatter/simple');
+
+// Prepare the test
+let logObject, colorFormatter, plainFormatter, formattedString, isoDate;
+
+tap.plan(12);
+
+logObject = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 3,
+  _levelString: 'error',
+  message: 'testing stuff!'
+};
+isoDate = (new Date(logObject._timestamp)).toISOString();
+
+plainFormatter = new SimpleFormatter();
+colorFormatter = new SimpleFormatter({
+  colorize: true
+});
+
+/**
+ * TEST: #format() - plain
+ */
+
+formattedString = plainFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in plain mode');
+
+tap.ok(formattedString.match(logObject._from),
+         '#format() should include the from property in plain mode');
+
+tap.ok(formattedString.match(isoDate),
+         '#format() should include the timestamp property in iso format in plain mode');
+
+tap.ok(formattedString.match(logObject._levelString),
+         '#format() should include the level string property in plain mode');
+
+tap.ok(formattedString.match(logObject.message),
+         '#format() should include the message property in plain mode');
+
+/**
+ * TEST: #format() - colorized
+ */
+
+formattedString = colorFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in color mode');
+
+tap.ok(formattedString.match(logObject._from),
+         '#format() should include the from property in color mode');
+
+tap.ok(formattedString.match(isoDate),
+         '#format() should include the timestamp property in iso format in color mode');
+
+tap.ok(formattedString.match(logObject._levelString),
+         '#format() should include the level string property in color mode');
+
+tap.ok(formattedString.match(logObject.message),
+         '#format() should include the message property in color mode');
+
+tap.equal(formattedString.split(String.fromCharCode(27) + '[31m').length, 2,
+         '#format() should colorize the string');
+
+tap.equal(formattedString.split(String.fromCharCode(27) + '[0m').length, 2,
+         '#format() should colorize only a bit of the string');
diff --git a/test/cologne/formatter/token.js b/test/cologne/formatter/token.js
new file mode 100644 (file)
index 0000000..9bc473d
--- /dev/null
@@ -0,0 +1,107 @@
+'use strict';
+
+let tap = require('tap');
+
+let TokenFormatter = require('../../../lib/cologne/formatter/token');
+
+// Prepare the test
+let logObject, defaultFormatter, customFormatter, ansiFormatter,
+  plainDateFormatter, customSearchFormatter, formattedString, isoDate;
+
+tap.plan(13);
+
+logObject = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 3,
+  _levelString: 'error',
+  message: 'testing stuff!'
+};
+isoDate = (new Date(logObject._timestamp)).toISOString();
+
+defaultFormatter = new TokenFormatter();
+customFormatter = new TokenFormatter({
+  formatString: '{{_level}} {{_cologneLog}} {{_timestamp}}'
+});
+ansiFormatter = new TokenFormatter({
+  formatString: 'string {{_ansi:red}}with color:{{_ansi:reset}} {{message}}'
+});
+plainDateFormatter = new TokenFormatter({
+  isoDate: false,
+  formatString: '{{_timestamp}}'
+});
+customSearchFormatter = new TokenFormatter({
+  formatString: '[[message]]',
+  replaceRule: /\[\[(.*?)\]\]/g
+});
+
+/**
+ * TEST: #format() - default
+ */
+
+formattedString = defaultFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in default mode');
+
+tap.equal(formattedString, logObject.message,
+         '#format() should include the message in default mode');
+
+/**
+ * TEST: #format() - custom
+ */
+
+formattedString = customFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in custom mode');
+
+tap.ok(formattedString.match(logObject._level),
+         '#format() with custom string should include the specified tokens (check 1)');
+
+tap.ok(formattedString.match(logObject._cologneLog),
+         '#format() with custom string should include the specified tokens (check 2)');
+
+tap.ok(formattedString.match(isoDate),
+         '#format() with iso date should include the timestamp as an iso date');
+
+/**
+ * TEST: #format() - ansi
+ */
+
+formattedString = ansiFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in ansi mode');
+
+tap.equal(formattedString.split(String.fromCharCode(27) + '[31m').length, 2,
+         '#format() with ansi tokens should colorize the string');
+
+tap.equal(formattedString.split(String.fromCharCode(27) + '[0m').length, 2,
+         '#format() with ansi reset should reset the string');
+
+
+/**
+ * TEST: #format() - plain date
+ */
+
+formattedString = plainDateFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in plain date mode');
+
+tap.equal(formattedString, logObject._timestamp.toString(),
+         '#format() with plain date should include the timestamp as-is');
+
+/**
+ * TEST: #format() - custom search
+ */
+
+formattedString = customSearchFormatter.format(logObject);
+
+tap.equal(typeof formattedString, 'string',
+         '#format() should output a string in custom search mode');
+
+tap.equal(formattedString, logObject.message,
+         '#format() with a custom search, should properly match the new tokens');
diff --git a/test/cologne/log_utilities.js b/test/cologne/log_utilities.js
new file mode 100644 (file)
index 0000000..e372683
--- /dev/null
@@ -0,0 +1,69 @@
+'use strict';
+
+let tap = require('tap');
+
+let LogUtilities = require('../../lib/cologne/log_utilities');
+
+// Prepare the test
+let t1, t2, preciseTime, regularObject, circularObject,
+  regularStringify, cologneStringify, circularStringify;
+
+tap.plan(7);
+
+regularObject = {
+  a: 1,
+  b: {
+    c: 'true',
+    d: false
+  }
+};
+
+circularObject = {
+  a: 1,
+  b: {
+    c: 'true',
+    d: false
+  }
+};
+circularObject.b.circular = circularObject;
+
+/**
+ * TEST: ::now()
+ */
+t1 = Date.now();
+preciseTime = LogUtilities.now();
+t2 = Date.now();
+
+// This test is sloppy :(
+tap.ok(Math.abs(t1 - preciseTime) < 1,
+       '::now() should give a precise timestamp (before)');
+tap.ok(Math.abs(t2 - preciseTime) < 1,
+       '::now() should give a precise timestamp (after)');
+
+/**
+ * TEST: ::stringify()
+ */
+
+regularStringify = JSON.stringify(regularObject);
+cologneStringify = LogUtilities.stringify(regularObject);
+circularStringify = LogUtilities.stringify(circularObject);
+
+tap.equal(regularStringify, cologneStringify,
+         '::stringify() should behave like JSON.stringify for non-circular objects');
+tap.equal(typeof JSON.parse(circularStringify).b.circular, 'string',
+         '::stringify() should replace circular references with a string');
+
+/**
+ * TEST: ::getAnsiCode()
+ */
+
+// NOTE: This isn't even trying to be a complete test... Just testing
+// that we get other than reset if valid, and the same as reset for all
+// invalid ones. knowing that reset is [0m
+
+tap.equal(LogUtilities.getAnsiCode('reset'), '[0m',
+          '::getAnsiCode is sending the correct reset code');
+tap.equal(LogUtilities.getAnsiCode('someRandomString'), LogUtilities.getAnsiCode('reset'),
+          '::getAnsiCode() should give us a reset code if something weird is sent');
+tap.notEqual(LogUtilities.getAnsiCode('red'), LogUtilities.getAnsiCode('reset'),
+          '::getAnsiCode() should give us a non-reset code if it\'s something real');
diff --git a/test/cologne/logger/console.js b/test/cologne/logger/console.js
new file mode 100644 (file)
index 0000000..ffc1876
--- /dev/null
@@ -0,0 +1,110 @@
+'use strict';
+
+let tap = require('tap');
+
+let ConsoleLogger = require('../../../lib/cologne/logger/console');
+
+// Prepare the test
+let dummyFormatter, dummyConsole, logObjectA, logObjectB, regularLogger,
+  overrideLogger, formattedLogger, params;
+
+dummyFormatter = {
+  values: [],
+  format: function (logObject) {
+    this.values.push(logObject);
+    return 'lol';
+  }
+};
+
+dummyConsole = {
+  values: {},
+  _log: function (type, args) {
+    this.values[type] = args;
+  },
+  log: function() {
+    this._log('log', Array.prototype.splice.call(arguments, [0]));
+  },
+  warn: function() {
+    this._log('warn', Array.prototype.splice.call(arguments, [0]));
+  },
+  error: function() {
+    this._log('error', Array.prototype.splice.call(arguments, [0]));
+  },
+  info: function() {
+    this._log('info', Array.prototype.splice.call(arguments, [0]));
+  }
+};
+
+logObjectA = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 6,
+  _levelString: 'info',
+  message: 'MessageOne'
+};
+
+logObjectB = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 6,
+  _levelString: 'info',
+  message: 'MessageTwo'
+};
+
+params = [logObjectA, logObjectB];
+
+regularLogger = new ConsoleLogger({});
+overrideLogger = new ConsoleLogger({
+  console: dummyConsole
+});
+formattedLogger = new ConsoleLogger({
+  console: dummyConsole,
+  formatter: dummyFormatter
+});
+
+/**
+ * TEST: #log() - regular
+ */
+
+tap.equal(regularLogger.console, global.console,
+          'It should default to the global console');
+
+/**
+ * TEST: #log() - override
+ */
+
+logObjectA._level = 5;
+logObjectB._level = 6;
+overrideLogger.log.apply(overrideLogger, params); // should go to info
+
+logObjectA._level = 4;
+logObjectB._level = 4;
+overrideLogger.log.apply(overrideLogger, params); // should go to warn
+
+logObjectA._level = 1;
+logObjectB._level = 3;
+overrideLogger.log.apply(overrideLogger, params); // should go to error
+
+logObjectA._level = 7;
+logObjectB._level = 7;
+overrideLogger.log.apply(overrideLogger, params); // should go to log
+
+tap.equal(dummyConsole.values.log.length, params.length,
+         'It should send debug messages to console\'s #log');
+tap.equal(dummyConsole.values.info.length, params.length,
+         'It should send info and notice messages to console\'s #info');
+tap.equal(dummyConsole.values.warn.length, params.length,
+         'It should send warn messages to console\'s #warn');
+tap.equal(dummyConsole.values.error.length, params.length,
+         'It should send error messages to console\'s #error');
+
+/**
+ * TEST: #log() - with formatter
+ */
+
+formattedLogger.log.apply(formattedLogger, params); // should go to log
+
+tap.similar(dummyFormatter.values, params,
+         'If available, it should send the objects to the formatter');
diff --git a/test/cologne/logger/file.js b/test/cologne/logger/file.js
new file mode 100644 (file)
index 0000000..01be09f
--- /dev/null
@@ -0,0 +1,103 @@
+'use strict';
+
+let fs = require('fs');
+
+let tap = require('tap');
+
+let FileLogger = require('../../../lib/cologne/logger/file');
+
+// Prepare the test
+let logObjectA, logObjectB, rawFile, formatterFile, rawLogger, formatterLogger,
+  params, dummyFormatter, formatterString;
+
+rawFile = './raw.log';
+formatterFile = './formatter.log';
+formatterString = 'example';
+
+logObjectA = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 6,
+  _levelString: 'info',
+  message: 'MessageOne'
+};
+
+logObjectB = {
+  _timestamp: Date.now() + .134,
+  _cologneLog: '1.0.0',
+  _from: 'Dummy Logger',
+  _level: 6,
+  _levelString: 'info',
+  message: 'MessageTwo'
+};
+
+params = [logObjectA, logObjectB];
+
+
+dummyFormatter = {
+  values: [],
+  format: function (logObject) {
+    this.values.push(logObject);
+    return formatterString;
+  }
+};
+
+rawLogger = new FileLogger({
+  file: rawFile
+});
+formatterLogger = new FileLogger({
+  file: formatterFile,
+  formatter: dummyFormatter
+});
+
+/**
+ * TEST: #log() - regular
+ */
+
+rawLogger.log.apply(rawLogger, params);
+
+setTimeout(function () {
+  tap.test('raw file', function (t) {
+    fs.readFile(rawFile, {encoding: 'utf8'}, function (error, contents) {
+      let lines;
+
+      lines = contents.trim().split('\n');
+
+      tap.equal(lines.length, params.length,
+               'it should send all params to the file');
+
+      tap.equal(JSON.stringify(logObjectA), lines[0],
+               'it should log the raw json object');
+
+      fs.unlink(rawFile, function () {
+        t.end();
+      });
+    });
+  });
+}, 10); // allow for flush
+/**
+ * TEST: #log() - formatter
+ */
+
+formatterLogger.log.apply(formatterLogger, params);
+
+setTimeout(function () {
+  tap.test('formatted file', function (t) {
+    fs.readFile(formatterFile, {encoding: 'utf8'}, function (error, contents) {
+      let lines;
+
+      lines = contents.trim().split('\n');
+
+      tap.equal(lines.length, params.length,
+               'it should send all params to the file');
+
+      tap.equal(formatterString, lines[0],
+               'it should log the formatted object');
+
+      fs.unlink(formatterFile, function () {
+        t.end();
+      });
+    });
+  });
+}, 10);