github.com/jiasir/deis@v1.12.2/logger/syslogish/server.go (about)

     1  package syslogish
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"net"
     8  	"regexp"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/deis/deis/logger/drain"
    13  	"github.com/deis/deis/logger/storage"
    14  )
    15  
    16  const queueSize = 500
    17  
    18  var appRegex *regexp.Regexp
    19  
    20  func init() {
    21  	appRegex = regexp.MustCompile(`^.* ([-_a-z0-9]+)\[[a-z0-9-_\.]+\].*`)
    22  }
    23  
    24  // Server implements a UDP-based "syslog-like" server.  Like syslog, as described by RFC 3164, it
    25  // expects that each packet contains a single log message and that, conversely, log messages are
    26  // encapsulated in their entirety by a single packet, however, no attempt is made to parse the
    27  // messages received or validate that they conform to the specification.
    28  type Server struct {
    29  	conn           net.PacketConn
    30  	listening      bool
    31  	storageQueue   chan string
    32  	storageAdapter storage.Adapter
    33  	drainageQueue  chan string
    34  	drain          drain.LogDrain
    35  	adapterMutex   sync.RWMutex
    36  	drainMutex     sync.RWMutex
    37  }
    38  
    39  // NewServer returns a pointer to a new Server instance.
    40  func NewServer(bindHost string, bindPort int) (*Server, error) {
    41  	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindHost, bindPort))
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	c, err := net.ListenUDP("udp", addr)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return &Server{
    50  		conn:          c,
    51  		storageQueue:  make(chan string, queueSize),
    52  		drainageQueue: make(chan string, queueSize),
    53  	}, nil
    54  }
    55  
    56  // SetStorageAdapter permits a server's underlying storage.Adapter to be reconfigured (replaced)
    57  // at runtime.
    58  func (s *Server) SetStorageAdapter(storageAdapter storage.Adapter) {
    59  	// Get an exclusive lock before updating the internal pointer to the storage adapter.  Other
    60  	// goroutines holding read locks might depend on that pointer as it currently exists.
    61  	s.adapterMutex.Lock()
    62  	defer s.adapterMutex.Unlock()
    63  	s.storageAdapter = storageAdapter
    64  }
    65  
    66  // SetDrain permits a server's underlying drain.LogDrain to be reconfigured (replaced) at runtime.
    67  func (s *Server) SetDrain(drain drain.LogDrain) {
    68  	// Get an exclusive lock before updating the internal pointer to the log drain.  Other
    69  	// goroutines holding read locks might depend on that pointer as it currently exists.
    70  	s.drainMutex.Lock()
    71  	defer s.drainMutex.Unlock()
    72  	s.drain = drain
    73  }
    74  
    75  // Listen starts the server's main loop.
    76  func (s *Server) Listen() {
    77  	// Should only ever be called once
    78  	if !s.listening {
    79  		s.listening = true
    80  		go s.receive()
    81  		go s.processStorage()
    82  		go s.processDrainage()
    83  		log.Println("syslogish server running")
    84  	}
    85  }
    86  
    87  func (s *Server) receive() {
    88  	// Make buffer the same size as the max for a UDP packet
    89  	buf := make([]byte, 65535)
    90  	for {
    91  		n, _, err := s.conn.ReadFrom(buf)
    92  		if err != nil {
    93  			log.Fatal("syslogish server read error", err)
    94  		}
    95  		message := strings.TrimSuffix(string(buf[:n]), "\n")
    96  		select {
    97  		case s.storageQueue <- message:
    98  		default:
    99  		}
   100  	}
   101  }
   102  
   103  func (s *Server) processStorage() {
   104  	for message := range s.storageQueue {
   105  		app, err := getAppName(message)
   106  		if err != nil {
   107  			log.Println(err)
   108  			return
   109  		}
   110  		// Get a read lock to ensure the storage adapater pointer can't be nilled by the configurer
   111  		// in the time between we check if it's nil and the time we invoke .Write() upon it.
   112  		s.adapterMutex.RLock()
   113  		// DONT'T defer unlocking... defered statements are executed when the function returns, but
   114  		// we are inside an infinite loop here.  If we defer, we would never release the lock.
   115  		// Instead, release it manually below.
   116  		if s.storageAdapter != nil {
   117  			s.storageAdapter.Write(app, message)
   118  			// We don't bother trapping errors here, so failed writes to storage are silent.  This is by
   119  			// design.  If we sent a log message to STDOUT in response to the failure, deis-logspout
   120  			// would read it and forward it back to deis-logger, which would fail again to write to
   121  			// storage and spawn ANOTHER log message.  The effect would be an infinite loop of
   122  			// unstoreable log messages that would nevertheless fill up journal logs and eventually
   123  			// overake the disk.
   124  			//
   125  			// Treating this as a fatal event would cause the deis-logger unit to restart-- sending
   126  			// even more log messages to STDOUT.  The overall effect would be the same as described
   127  			// above with the added disadvantages of flapping.
   128  		}
   129  		s.adapterMutex.RUnlock()
   130  		// Add the message to the drainage queue.  This allows the storage loop to continue right
   131  		// away instead of waiting while the message is sent to an external service-- since that
   132  		// could be a bottleneck and error prone depending on rate limiting, network congestion, etc.
   133  		select {
   134  		case s.drainageQueue <- message:
   135  		default:
   136  		}
   137  	}
   138  }
   139  
   140  func (s *Server) processDrainage() {
   141  	for message := range s.drainageQueue {
   142  		// Get a read lock to ensure the drain pointer can't be nilled by the configurer in the time
   143  		// between we check if it's nil and the time we invoke .Send() upon it.
   144  		s.drainMutex.RLock()
   145  		// DONT'T defer unlocking... defered statements are executed when the function returns, but
   146  		// we are inside an infinite loop here.  If we defer, we would never release the lock.
   147  		// Instead, release it manually below.
   148  		if s.drain != nil {
   149  			s.drain.Send(message)
   150  			// We don't bother trapping errors here, so failed sends to the drain are silent.  This is
   151  			// by design.  If we sent a log message to STDOUT in response to the failure, deis-logspout
   152  			// would read it and forward it back to deis-logger, which would fail again to send to the
   153  			// drain and spawn ANOTHER log message.  The effect would be an infinite loop of undrainable
   154  			// log messages that would nevertheless fill up journal logs and eventually overake the disk.
   155  			//
   156  			// Treating this as a fatal event would cause the deis-logger unit to restart-- sending
   157  			// even more log messages to STDOUT.  The overall effect would be the same as described
   158  			// above with the added disadvantages of flapping.
   159  		}
   160  		s.drainMutex.RUnlock()
   161  	}
   162  }
   163  
   164  func getAppName(message string) (string, error) {
   165  	match := appRegex.FindStringSubmatch(message)
   166  	if match == nil {
   167  		return "", fmt.Errorf("Could not find app name in message: %s", message)
   168  	}
   169  	return match[1], nil
   170  }
   171  
   172  // ReadLogs returns a specified number of log lines (if available) for a specified app by
   173  // delegating to the server's underlying storage.Adapter.
   174  func (s *Server) ReadLogs(app string, lines int) ([]string, error) {
   175  	// Get a read lock to ensure the storage adapater pointer can't be updated by another
   176  	// goroutine in the time between we check if it's nil and the time we invoke .Read() upon
   177  	// it.
   178  	s.adapterMutex.RLock()
   179  	defer s.adapterMutex.RUnlock()
   180  	if s.storageAdapter == nil {
   181  		return nil, fmt.Errorf("Could not find logs for '%s'.  No storage adapter specified.", app)
   182  	}
   183  	return s.storageAdapter.Read(app, lines)
   184  }
   185  
   186  // DestroyLogs deletes all logs for a specified app by delegating to the server's underlying
   187  // storage.Adapter.
   188  func (s *Server) DestroyLogs(app string) error {
   189  	// Get a read lock to ensure the storage adapater pointer can't be updated by another
   190  	// goroutine in the time between we check if it's nil and the time we invoke .Destroy() upon
   191  	// it.
   192  	s.adapterMutex.RLock()
   193  	defer s.adapterMutex.RUnlock()
   194  	if s.storageAdapter == nil {
   195  		return fmt.Errorf("Could not destroy logs for '%s'.  No storage adapter specified.", app)
   196  	}
   197  	return s.storageAdapter.Destroy(app)
   198  }
   199  
   200  // ReopenLogs delegate to the server's underlying storage.Adapter to, if applicable, refresh
   201  // references to underlying storage mechanisms.  This is useful, for instance, to ensure logging
   202  // continues smoothly after log rotation when file-based storage is in use.
   203  func (s *Server) ReopenLogs() error {
   204  	// Get a read lock to ensure the storage adapater pointer can't be updated by another
   205  	// goroutine in the time between we check if it's nil and the time we invoke .Reopen() upon
   206  	// it.
   207  	s.adapterMutex.RLock()
   208  	defer s.adapterMutex.RUnlock()
   209  	if s.storageAdapter == nil {
   210  		return errors.New("Could not reopen logs.  No storage adapter specified.")
   211  	}
   212  	return s.storageAdapter.Reopen()
   213  }