// run executes the application. func (a *application) run(ctx context.Context) error { // Setup common context parameters. ctx, cancelFunc := context.WithCancel(ctx) // Monitor our shutdown singal. Cancel our context if it is closed. go func() { <-a.shutdownC log.Infof(ctx, "Shutdown signal received; cancelling context.") cancelFunc() }() wg := sync.WaitGroup{} for i := 0; i < a.config.numWorkers; i++ { i := i wg.Add(1) go func(id int) { defer wg.Done() a.process(log.SetField(ctx, "worker", i)) }(i) } wg.Wait() return nil }
func (s *endpointServiceImpl) send(ctx context.Context, data []byte) error { ctx = log.SetField(ctx, "endpointURL", s.url) return retryCall(ctx, "endpoint.send", func() error { startTime := clock.Now(ctx) log.Debugf(ctx, "Pushing message to endpoint.") req, err := http.NewRequest("POST", s.url, bytes.NewReader(data)) if err != nil { log.Errorf(log.SetError(ctx, err), "Failed to create HTTP request.") return err } req.Header.Add("content-type", protobufContentType) req.Header.Add("user-agent", monitoringEndpointUserAgent) resp, err := s.client.Do(req) if err != nil { // Treat a client error as transient. log.Warningf(log.SetError(ctx, err), "Failed proxy client request.") return errors.WrapTransient(err) } defer resp.Body.Close() // Read the full response body. This will enable us to re-use the // connection. bodyData, err := ioutil.ReadAll(resp.Body) if err != nil { log.Errorf(log.SetError(ctx, err), "Error during endpoint connection.") return errors.WrapTransient(err) } log.Fields{ "status": resp.Status, "statusCode": resp.StatusCode, "headers": resp.Header, "contentLength": resp.ContentLength, "body": string(bodyData), "duration": clock.Now(ctx).Sub(startTime), }.Debugf(ctx, "Received HTTP response from endpoint.") if http.StatusOK <= resp.StatusCode && resp.StatusCode < http.StatusMultipleChoices { log.Debugf(ctx, "Message pushed successfully.") return nil } err = fmt.Errorf("http: server error (%d)", resp.StatusCode) if resp.StatusCode >= http.StatusInternalServerError { err = errors.WrapTransient(err) } log.Fields{ log.ErrorKey: err, "status": resp.Status, "statusCode": resp.StatusCode, }.Warningf(ctx, "Proxy error.") return err }) }
// mainImpl is the main execution function. func mainImpl(args []string) int { // Use all of teh corez. if os.Getenv("GOMAXPROCS") == "" { runtime.GOMAXPROCS(runtime.NumCPU()) } // Install a console logger by default. ctx := context.Background() ctx = gologger.Use(ctx) loggerConfig := newLoggerConfig() // Internal logging config (cloud logging). logConfig := log.Config{Level: log.Debug} config := config{} fs := flag.CommandLine config.addFlags(fs) loggerConfig.addFlags(fs) logConfig.AddFlags(fs) fs.Parse(args) // TODO(dnj): Fix this once LUCI logging CL lands. ctx = log.SetLevel(ctx, logConfig.Level) ctx = logConfig.Set(ctx) // Load authenticated client. client, err := config.createAuthenticatedClient(ctx) if err != nil { log.Errorf(log.SetError(ctx, err), "Failed to create authenticated service client.") return 1 } // Setup local logging configuration. ctx, logFlushFunc, err := loggerConfig.use(ctx, client) if err == nil { defer logFlushFunc() } else { log.Warningf(log.SetError(ctx, err), "Failed to setup cloud logging.") } app := newApplication(config) if err := app.loadServices(ctx, client); err != nil { log.Errorf(log.SetError(ctx, err), "Failed to initialize services.") return 1 } // Set up interrupt handler. signalC := make(chan os.Signal, 1) go func() { triggered := false for sig := range signalC { if !triggered { triggered = true log.Infof(log.SetField(ctx, "signal", sig), "Received signal; starting shutdown.") app.shutdown() } else { // Triggered multiple times; immediately shut down. os.Exit(1) } } }() signal.Notify(signalC, os.Interrupt, os.Kill) defer func() { signal.Stop(signalC) close(signalC) }() log.Infof(ctx, "Starting application execution...") if err := app.run(ctx); err != nil { log.Errorf(log.SetError(ctx, err), "Error during application execution.") return 1 } return 0 }