Skip to content

Instantly share code, notes, and snippets.

@chuckhacker
Created June 29, 2017 17:22
Show Gist options
  • Select an option

  • Save chuckhacker/b5f2503282002f9e6e6ac9e703b760b6 to your computer and use it in GitHub Desktop.

Select an option

Save chuckhacker/b5f2503282002f9e6e6ac9e703b760b6 to your computer and use it in GitHub Desktop.
package main
import (
"errors"
"fmt"
"net"
"os"
"strconv"
"strings"
"time"
)
var logDNAkey string = "<REDACTED>"
const (
TCP = "tcp"
TCP_TLS = "tcp_tls"
UDP = "udp"
)
const (
LOG_LEVEL_EMERG = 0
LOG_LEVEL_ALERT = 1
LOG_LEVEL_CRIT = 2
LOG_LEVEL_ERR = 3
LOG_LEVEL_WARNING = 4
LOG_LEVEL_NOTICE = 5
LOG_LEVEL_INFO = 6
LOG_LEVEL_DEBUG = 7
)
var LOG_DEFAULT_FACILITY int64 = 3 // DAEMON
type Writer struct {
Port int
Network string
Server string
Program string
Conn net.Conn
}
func constructRemoteLogMessage(message, program string) string {
// fmt.Printf("constructRemoteLogMessage: message=%s program=%s\n", message, program)
words := strings.Fields(message)
levelStr := "UNKNOWN"
component := program
logMessage := message
if len(words) >= 3 {
// return message // well that sucks, maybe just try to format it?
component = words[0]
levelStr = words[1]
logMessage = strings.Join(words[2:], " ")
}
levelInt := LOG_LEVEL_INFO
switch levelStr {
case "EMERG":
levelInt = LOG_LEVEL_EMERG
case "ALERT":
levelInt = LOG_LEVEL_ALERT
case "CRIT":
levelInt = LOG_LEVEL_CRIT
case "ERR":
levelInt = LOG_LEVEL_ERR
case "ERRO":
levelInt = LOG_LEVEL_ERR
case "ERROR":
levelInt = LOG_LEVEL_ERR
case "WARNING":
levelInt = LOG_LEVEL_WARNING
case "WARN": // stupider shit has been done before
levelInt = LOG_LEVEL_WARNING
case "NOTICE":
levelInt = LOG_LEVEL_NOTICE
case "INFO":
levelInt = LOG_LEVEL_INFO
case "DEBUG":
levelInt = LOG_LEVEL_DEBUG
default:
// fmt.Println("Unknown log level: " + levelStr)
levelInt = LOG_LEVEL_INFO
// back to the drawing board
component = program
levelStr = "INFO"
logMessage = message
}
levelProduct := int64(levelInt) + LOG_DEFAULT_FACILITY*8 // level + LOG_DEFAULT_FACILITY*8
levelProductStr := strconv.FormatInt(levelProduct, 10)
pidInt := os.Getpid()
pidStr := strconv.FormatInt(int64(pidInt), 10)
timestamp := time.Now().UTC().Format(time.RFC3339)
hostName, err := os.Hostname()
if err != nil {
hostName = "localhost"
fmt.Println("os.Hostname() error: " + err.Error())
}
pid := pidStr + " "
msgID := component + " "
var keyStr = "<key:" + logDNAkey + "> "
return keyStr + "<" + levelProductStr + ">1 " + timestamp + " " + hostName + " " + program + " " + pid + msgID + "- " + logMessage + "\n"
}
// TODO: do all of these things asynchronously?!
func (w *Writer) Dial() error {
address := fmt.Sprintf("%s:%d", w.Server, w.Port)
conn, err := net.Dial(TCP, address)
w.Conn = conn
if err != nil {
fmt.Println("TCP dial error: " + err.Error())
}
if conn == nil {
fmt.Println("TCP connection error")
}
return err
}
// TODO: maybe it makes sense to use UDP instead here...
func (w *Writer) Write(p []byte) (n int, err error) {
logMessage := constructRemoteLogMessage(string(p[:]), w.Program)
if w.Conn != nil {
n, result := w.Conn.Write([]byte(logMessage))
if result != nil {
fmt.Println("TCP write error: " + result.Error() + ", should re-dial but won't...")
// w.Dial()
}
return n, result
} else {
fmt.Println("TCP connection error, nil Conn, should re-dial but won't...")
// w.Dial()
}
return 0, errors.New("TCP write error")
}
func (w *Writer) HangUp() error {
err := w.Conn.Close()
if err != nil {
fmt.Println("TCP hang up error: " + err.Error())
}
return err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment