]> 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
 
 
        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
 
 
        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.
 * **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
 
 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
 
 ```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);
 ```
 
 
 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:
 
 ```
 
 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.
 
 ```
 
 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`
   formatString. Defaults to `/{{(.*?)}}/g`
-* `separatorLength` <Number> : How long to print separators. Defaults to 60.
 * `isoDate` <Boolean> : Whether or not to convert `_timestamp` to ISO
 * `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
 
 ```javascript
-// A valid, very minimalistic formatter
 var simpleFormatter = {
 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",
   "author": {
     "name": "Ben Beltran",
     "email": "ben@nsovocal.com",
-    "url": "http://nsovocal.com"
+    "url": "http://unlimited.pizza"
   },
   "repository": {
     "type": "git",
   },
   "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": {
   "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": {
   },
   "devDependencies": {
-    "tellurium": "2.0.x"
+    "tap": "5.1.x",
+    "jsdoc-augmented-template": "rbdr/jsdoc-augmented-template"
   },
   "engines": {
   },
   "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);