github.com/mundipagg/tracer-splunk-writer@v1.0.6/splunk.go (about)

     1  package splunk
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"os"
     8  	"regexp"
     9  	"sync"
    10  	"time"
    11  
    12  	"strings"
    13  
    14  	jsoniter "github.com/json-iterator/go"
    15  	"github.com/mralves/tracer"
    16  	"github.com/mundipagg/tracer-splunk-writer/buffer"
    17  	"github.com/mundipagg/tracer-splunk-writer/json"
    18  	s "github.com/mundipagg/tracer-splunk-writer/strings"
    19  )
    20  
    21  type Writer struct {
    22  	sync.Locker
    23  	address                 string
    24  	key                     string
    25  	configLineLog           map[string]interface{}
    26  	defaultPropertiesSplunk map[string]interface{}
    27  	defaultPropertiesApp    map[string]interface{}
    28  	client                  *http.Client
    29  	buffer                  buffer.Buffer
    30  	minimumLevel            uint8
    31  	marshaller              jsoniter.API
    32  	messageEnvelop          string
    33  }
    34  
    35  var punctuation = regexp.MustCompile(`(.+?)[?;:\\.,!]?$`)
    36  
    37  //Used when message contains properties to replace.
    38  var r = strings.NewReplacer("{", "{{.", "}", "}}")
    39  
    40  func (sw *Writer) Write(entry tracer.Entry) {
    41  	go func(sw *Writer, entry tracer.Entry) {
    42  		defer func() {
    43  			if err := recover(); err != nil {
    44  				stderr("COULD NOT SEND SPLUNK TO SPLUNK BECAUSE %v", err)
    45  			}
    46  		}()
    47  		if entry.Level > sw.minimumLevel {
    48  			return
    49  		}
    50  
    51  		extraProperties := map[string]interface{}{
    52  			"RequestKey": entry.TransactionId,
    53  		}
    54  
    55  		if s.IsBlank(entry.TransactionId) {
    56  			delete(extraProperties, "RequestKey")
    57  		}
    58  
    59  		properties := NewEntry(append(entry.Args, sw.defaultPropertiesApp, extraProperties))
    60  
    61  		message := punctuation.FindStringSubmatch(s.Capitalize(entry.Message))[1]
    62  
    63  		message, err := s.ProcessString(r.Replace(message), properties)
    64  		if err != nil {
    65  			message = entry.Message
    66  		}
    67  
    68  		l := NewEntry(sw.configLineLog)
    69  		l.Add("time", time.Now().UTC().UnixNano())
    70  		e := NewEntry(Entry{
    71  			"AdditionalData": properties,
    72  			"Message":        message,
    73  			"Severity":       Level(entry.Level),
    74  		}, sw.defaultPropertiesSplunk)
    75  		l.Add("event", e)
    76  
    77  		sw.buffer.Write(l)
    78  	}(sw, entry)
    79  }
    80  
    81  func (sw *Writer) send(events []interface{}) error {
    82  	defer func() {
    83  		err := recover()
    84  		if err != nil {
    85  			fmt.Printf("%v\n", err)
    86  		}
    87  	}()
    88  
    89  	body, err := sw.marshaller.Marshal(events)
    90  	if err != nil {
    91  		stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", err)
    92  		return err
    93  	}
    94  
    95  	request, _ := http.NewRequest(http.MethodPost, sw.address, bytes.NewBuffer(body))
    96  	if len(sw.key) > 0 {
    97  		request.Header.Set("Authorization", "Splunk "+sw.key)
    98  	}
    99  	request.Header.Set("Content-Type", "application/json")
   100  
   101  	var response *http.Response
   102  	response, err = sw.client.Do(request)
   103  	if err != nil {
   104  		stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", err)
   105  		return err
   106  	}
   107  	response.Body.Close()
   108  	if response.StatusCode != 200 {
   109  		stderr("COULD NOT SEND LOG TO SPLUNK BECAUSE %v", response.Status)
   110  		return fmt.Errorf("request returned %v", response.StatusCode)
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func stderr(message string, args ...interface{}) {
   117  	fmt.Fprintf(os.Stderr, message+"\n", args...)
   118  }
   119  
   120  type Config struct {
   121  	Address                 string
   122  	Key                     string
   123  	Application             string
   124  	Buffer                  buffer.Config
   125  	MinimumLevel            uint8
   126  	Timeout                 time.Duration
   127  	ConfigLineLog           Entry
   128  	DefaultPropertiesSplunk Entry
   129  	DefaultPropertiesApp    Entry
   130  	MessageEnvelop          string
   131  }
   132  
   133  func New(config Config) *Writer {
   134  	writer := Writer{
   135  		Locker:  &sync.RWMutex{},
   136  		address: config.Address,
   137  		key:     config.Key,
   138  		client: &http.Client{
   139  			Timeout: config.Timeout,
   140  			Transport: &http.Transport{
   141  				TLSHandshakeTimeout: config.Timeout,
   142  				IdleConnTimeout:     config.Timeout,
   143  			},
   144  		},
   145  		messageEnvelop:          config.MessageEnvelop,
   146  		minimumLevel:            config.MinimumLevel,
   147  		configLineLog:           config.ConfigLineLog,
   148  		defaultPropertiesSplunk: config.DefaultPropertiesSplunk,
   149  		defaultPropertiesApp:    config.DefaultPropertiesApp,
   150  		marshaller:              json.NewWithCaseStrategy(s.UseAnnotation),
   151  	}
   152  	config.Buffer.OnOverflow = writer.send
   153  	writer.buffer = buffer.New(config.Buffer)
   154  	return &writer
   155  }