Merge pull request #21 from rnapier/master

Add Windows/FreeBSD support
diff --git a/README.md b/README.md
index 90cdb79..d8efaf3 100644
--- a/README.md
+++ b/README.md
@@ -107,11 +107,15 @@
 #### Hooks
 
 You can add hooks for logging levels. For example to send errors to an exception
-tracking service on `Error`, `Fatal` and `Panic` or info to StatsD.
+tracking service on `Error`, `Fatal` and `Panic` or info to StatsD. Note this is
+not the real implementation of the Airbrake hook in logrus, just a sample.
 
 ```go
-log = logrus.New()
-log.Hooks.Add(new(AirbrakeHook))
+var log = logrus.New()
+
+func init() {
+  log.Hooks.Add(new(AirbrakeHook))
+}
 
 type AirbrakeHook struct{}
 
@@ -139,6 +143,23 @@
 }
 ```
 
+Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
+
+```go
+import (
+  "github.com/Sirupsen/logrus"
+  "github.com/Sirupsen/logrus/hooks/airbrake"
+)
+
+func init() {
+  log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
+}
+```
+
+* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go).
+  Send errors to an exception tracking service compatible with the Airbrake API.
+  Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
+
 #### Level logging
 
 Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
diff --git a/airbrake.go b/airbrake.go
new file mode 100644
index 0000000..69a151b
--- /dev/null
+++ b/airbrake.go
@@ -0,0 +1,43 @@
+package logrus
+
+import (
+	"github.com/tobi/airbrake-go"
+)
+
+// AirbrakeHook to send exceptions to an exception-tracking service compatible
+// with the Airbrake API. You must set:
+// * airbrake.Endpoint
+// * airbrake.ApiKey
+// * airbrake.Environment (only sends exceptions when set to "production")
+//
+// Before using this hook, to send exceptions. Entries that trigger an Error,
+// Fatal or Panic should now include an "Error" field to send to Airbrake.
+type AirbrakeHook struct{}
+
+func (hook *AirbrakeHook) Fire(entry *Entry) error {
+	if entry.Data["error"] == nil {
+		entry.Logger.WithFields(Fields{
+			"source":   "airbrake",
+			"endpoint": airbrake.Endpoint,
+		}).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
+		return nil
+	}
+
+	err := airbrake.Notify(entry.Data["error"].(error))
+	if err != nil {
+		entry.Logger.WithFields(Fields{
+			"source":   "airbrake",
+			"endpoint": airbrake.Endpoint,
+		}).Warn("Failed to send error to Airbrake")
+	}
+
+	return nil
+}
+
+func (hook *AirbrakeHook) Levels() []Level {
+	return []Level{
+		Error,
+		Fatal,
+		Panic,
+	}
+}
diff --git a/examples/basic/basic.go b/examples/basic/basic.go
new file mode 100644
index 0000000..3594550
--- /dev/null
+++ b/examples/basic/basic.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+	"github.com/Sirupsen/logrus"
+)
+
+var log = logrus.New()
+
+func init() {
+	log.Formatter = new(logrus.JSONFormatter)
+	log.Formatter = new(logrus.TextFormatter) // default
+}
+
+func main() {
+	log.WithFields(logrus.Fields{
+		"animal": "walrus",
+		"size":   10,
+	}).Info("A group of walrus emerges from the ocean")
+
+	log.WithFields(logrus.Fields{
+		"omg":    true,
+		"number": 122,
+	}).Warn("The group's number increased tremendously!")
+
+	log.WithFields(logrus.Fields{
+		"omg":    true,
+		"number": 100,
+	}).Fatal("The ice breaks!")
+}
diff --git a/examples/hook/hook.go b/examples/hook/hook.go
new file mode 100644
index 0000000..42e7a4c
--- /dev/null
+++ b/examples/hook/hook.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+	"github.com/Sirupsen/logrus"
+	"github.com/Sirupsen/logrus/hooks/airbrake"
+	"github.com/tobi/airbrake-go"
+)
+
+var log = logrus.New()
+
+func init() {
+	log.Formatter = new(logrus.TextFormatter) // default
+	log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
+}
+
+func main() {
+	airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
+	airbrake.ApiKey = "whatever"
+	airbrake.Environment = "production"
+
+	log.WithFields(logrus.Fields{
+		"animal": "walrus",
+		"size":   10,
+	}).Info("A group of walrus emerges from the ocean")
+
+	log.WithFields(logrus.Fields{
+		"omg":    true,
+		"number": 122,
+	}).Warn("The group's number increased tremendously!")
+
+	log.WithFields(logrus.Fields{
+		"omg":    true,
+		"number": 100,
+	}).Fatal("The ice breaks!")
+}
diff --git a/examples/text.go b/examples/text.go
deleted file mode 100644
index cc4bfec..0000000
--- a/examples/text.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
-	"os"
-
-	"github.com/Sirupsen/logrus"
-)
-
-func main() {
-	log := logrus.New()
-	if os.Getenv("LOG_FORMAT") == "json" {
-		log.Formatter = new(logrus.JSONFormatter)
-	} else {
-		log.Formatter = new(logrus.TextFormatter)
-	}
-
-	for {
-		log.WithFields(logrus.Fields{
-			"animal": "walrus",
-			"size":   10,
-		}).Print("A group of walrus emerges from the ocean")
-
-		log.WithFields(logrus.Fields{
-			"omg":    true,
-			"number": 122,
-		}).Warn("The group's number increased tremendously!")
-
-		log.WithFields(logrus.Fields{
-			"animal": "walrus",
-			"size":   10,
-		}).Print("A giant walrus appears!")
-
-		log.WithFields(logrus.Fields{
-			"animal": "walrus",
-			"size":   9,
-		}).Print("Tremendously sized cow enters the ocean.")
-
-		log.WithFields(logrus.Fields{
-			"omg":    true,
-			"number": 100,
-		}).Fatal("The ice breaks!")
-	}
-}
diff --git a/hooks/airbrake/airbrake.go b/hooks/airbrake/airbrake.go
new file mode 100644
index 0000000..1dff9b2
--- /dev/null
+++ b/hooks/airbrake/airbrake.go
@@ -0,0 +1,44 @@
+package logrus_airbrake
+
+import (
+	"github.com/Sirupsen/logrus"
+	"github.com/tobi/airbrake-go"
+)
+
+// AirbrakeHook to send exceptions to an exception-tracking service compatible
+// with the Airbrake API. You must set:
+// * airbrake.Endpoint
+// * airbrake.ApiKey
+// * airbrake.Environment (only sends exceptions when set to "production")
+//
+// Before using this hook, to send exceptions. Entries that trigger an Error,
+// Fatal or Panic should now include an "Error" field to send to Airbrake.
+type AirbrakeHook struct{}
+
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
+	if entry.Data["error"] == nil {
+		entry.Logger.WithFields(logrus.Fields{
+			"source":   "airbrake",
+			"endpoint": airbrake.Endpoint,
+		}).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
+		return nil
+	}
+
+	err := airbrake.Notify(entry.Data["error"].(error))
+	if err != nil {
+		entry.Logger.WithFields(logrus.Fields{
+			"source":   "airbrake",
+			"endpoint": airbrake.Endpoint,
+		}).Warn("Failed to send error to Airbrake")
+	}
+
+	return nil
+}
+
+func (hook *AirbrakeHook) Levels() []logrus.Level {
+	return []logrus.Level{
+		logrus.Error,
+		logrus.Fatal,
+		logrus.Panic,
+	}
+}