// LogWriter returns an environment-specific io.Writer suitable for passing // to log.SetOutput. It will also include writing to os.Stderr as well. func LogWriter() (w io.Writer) { w = os.Stderr if !env.OnGCE() { return } projID, err := metadata.ProjectID() if projID == "" { log.Printf("Error getting project ID: %v", err) return } scopes, _ := metadata.Scopes("default") haveScope := func(scope string) bool { for _, x := range scopes { if x == scope { return true } } return false } if !haveScope(logging.Scope) { log.Printf("when this Google Compute Engine VM instance was created, it wasn't granted enough access to use Google Cloud Logging (Scope URL: %v).", logging.Scope) return } logc, err := logging.NewClient(context.Background(), projID, "camlistored-stderr") if err != nil { log.Printf("Error creating Google logging client: %v", err) return } return io.MultiWriter(w, logc.Writer(logging.Debug)) }
func main() { if len(os.Args) == 2 { usage("Missing command.") } if len(os.Args) != 3 { usage("") } projID := os.Args[1] command := os.Args[2] // [START setup] ctx := context.Background() client, err := logging.NewClient(ctx, projID) if err != nil { log.Fatalf("Failed to create logging client: %v", err) } adminClient, err := logadmin.NewClient(ctx, projID) if err != nil { log.Fatalf("Failed to create logadmin client: %v", err) } client.OnError = func(err error) { // Print an error to the local log. // For example, if Flush() failed. log.Printf("client.OnError: %v", err) } // [END setup] switch command { case "write": log.Print("Writing some log entries.") writeEntry(client) structuredWrite(client) case "read": log.Print("Fetching and printing log entries.") entries, err := getEntries(adminClient, projID) if err != nil { log.Fatalf("Could not get entries: %v", err) } log.Printf("Found %d entries.", len(entries)) for _, entry := range entries { fmt.Printf("Entry: %6s @%s: %v\n", entry.Severity, entry.Timestamp.Format(time.RFC3339), entry.Payload) } case "delete": log.Print("Deleting log.") if err := deleteLog(adminClient); err != nil { log.Fatalf("Could not delete log: %v", err) } default: usage("Unknown command.") } }
func TestSimplelog(t *testing.T) { tc := testutil.SystemTest(t) ctx := context.Background() client, err := logging.NewClient(ctx, tc.ProjectID) if err != nil { t.Fatalf("logging.NewClient: %v", err) } adminClient, err := logadmin.NewClient(ctx, tc.ProjectID) if err != nil { t.Fatalf("logadmin.NewClient: %v", err) } defer func() { if err := client.Close(); err != nil { t.Errorf("Close: %v", err) } }() defer func() { testutil.Retry(t, 10, time.Second, func(r *testutil.R) { if err := deleteLog(adminClient); err != nil { r.Errorf("deleteLog: %v", err) } }) }() client.OnError = func(err error) { t.Errorf("OnError: %v", err) } writeEntry(client) structuredWrite(client) testutil.Retry(t, 10, time.Second, func(r *testutil.R) { entries, err := getEntries(adminClient, tc.ProjectID) if err != nil { r.Errorf("getEntries: %v", err) return } if got, want := len(entries), 2; got != want { r.Errorf("len(entries) = %d; want %d", got, want) return } wantContain := map[string]*logging.Entry{ "Anything": entries[0], "The payload can be any type!": entries[0], "infolog is a standard Go log.Logger": entries[1], } for want, entry := range wantContain { msg := fmt.Sprintf("%s", entry.Payload) if !strings.Contains(msg, want) { r.Errorf("want %q to contain %q", msg, want) } } }) }
func ExampleClient_Logger() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } lg := client.Logger("my-log") _ = lg // TODO: use the Logger. }
func ExampleLogger_Log() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } lg := client.Logger("my-log") lg.Log(logging.Entry{Payload: "something happened"}) }
func ExampleLogger_StandardLogger() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } lg := client.Logger("my-log") slg := lg.StandardLogger(logging.Info) slg.Println("an informative message") }
func ExampleClient_Ping() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } if err := client.Ping(ctx); err != nil { // TODO: Handle error. } }
func ExampleLogger_LogSync() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } lg := client.Logger("my-log") err = lg.LogSync(ctx, logging.Entry{Payload: "red alert"}) if err != nil { // TODO: Handle error. } }
func ExampleNewClient() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } // Use client to manage logs, metrics and sinks. // Close the client when finished. if err := client.Close(); err != nil { // TODO: Handle error. } }
func ExampleNewClient_errorFunc() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } // Print all errors to stdout. client.OnError = func(e error) { fmt.Fprintf(os.Stdout, "logging: %v", e) } // Use client to manage logs, metrics and sinks. // Close the client when finished. if err := client.Close(); err != nil { // TODO: Handle error. } }
func NewClient(ctx context.Context, projectID, serviceName, serviceVersion string, opts ...option.ClientOption) (*Client, error) { l, err := logging.NewClient(ctx, projectID, "errorreports", opts...) if err != nil { return nil, fmt.Errorf("creating Logging client: %v", err) } c := &Client{ loggingClient: l, projectID: projectID, RepanicDefault: true, serviceContext: map[string]string{ "service": serviceName, }, } if serviceVersion != "" { c.serviceContext["version"] = serviceVersion } return c, nil }
func maybeSetupGoogleCloudLogging() { if flagGCEProjectID == "" && flagGCELogName == "" && flagGCEJWTFile == "" { return } if flagGCEProjectID == "" || flagGCELogName == "" || flagGCEJWTFile == "" { exitf("All of --gce_project_id, --gce_log_name, and --gce_jwt_file must be specified for logging on Google Cloud Logging.") } jsonSlurp, err := ioutil.ReadFile(flagGCEJWTFile) if err != nil { exitf("Error reading --gce_jwt_file value: %v", err) } jwtConf, err := google.JWTConfigFromJSON(jsonSlurp, logging.Scope) if err != nil { exitf("Error reading --gce_jwt_file value: %v", err) } logc, err := logging.NewClient(context.Background(), flagGCEProjectID, flagGCELogName, option.WithHTTPClient(jwtConf.Client(context.Background()))) if err != nil { exitf("Error creating GCL client: %v", err) } log.SetOutput(io.MultiWriter(os.Stderr, logc.Writer(logging.Debug))) }
func TestPing(t *testing.T) { // Ping twice, in case the service's InsertID logic messes with the error code. ctx := context.Background() // The global client should be valid. if err := client.Ping(ctx); err != nil { t.Errorf("project %s: got %v, expected nil", testProjectID, err) } if err := client.Ping(ctx); err != nil { t.Errorf("project %s, #2: got %v, expected nil", testProjectID, err) } // nonexistent project c, _ := newClients(ctx, testProjectID+"-BAD") if err := c.Ping(ctx); err == nil { t.Errorf("nonexistent project: want error pinging logging api, got nil") } if err := c.Ping(ctx); err == nil { t.Errorf("nonexistent project, #2: want error pinging logging api, got nil") } // Bad creds. We cannot test this with the fake, since it doesn't do auth. if integrationTest { c, err := logging.NewClient(ctx, testProjectID, option.WithTokenSource(badTokenSource{})) if err != nil { t.Fatal(err) } if err := c.Ping(ctx); err == nil { t.Errorf("bad creds: want error pinging logging api, got nil") } if err := c.Ping(ctx); err == nil { t.Errorf("bad creds, #2: want error pinging logging api, got nil") } if err := c.Close(); err != nil { t.Fatalf("error closing client: %v", err) } } }
// New creates a new logger that logs to Google Cloud Logging using the application // default credentials. // // See https://developers.google.com/identity/protocols/application-default-credentials func New(info logger.Info) (logger.Logger, error) { initGCP() var project string if projectID != "" { project = projectID } if projectID, found := info.Config[projectOptKey]; found { project = projectID } if project == "" { return nil, fmt.Errorf("No project was specified and couldn't read project from the meatadata server. Please specify a project") } // Issue #29344: gcplogs segfaults (static binary) // If HOME is not set, logging.NewClient() will call os/user.Current() via oauth2/google. // However, in static binary, os/user.Current() leads to segfault due to a glibc issue that won't be fixed // in a short term. (golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341) // So we forcibly set HOME so as to avoid call to os/user/Current() if err := ensureHomeIfIAmStatic(); err != nil { return nil, err } c, err := logging.NewClient(context.Background(), project) if err != nil { return nil, err } lg := c.Logger("gcplogs-docker-driver") if err := c.Ping(context.Background()); err != nil { return nil, fmt.Errorf("unable to connect or authenticate with Google Cloud Logging: %v", err) } l := &gcplogs{ logger: lg, container: &containerInfo{ Name: info.ContainerName, ID: info.ContainerID, ImageName: info.ContainerImageName, ImageID: info.ContainerImageID, Created: info.ContainerCreated, Metadata: info.ExtraAttributes(nil), }, } if info.Config[logCmdKey] == "true" { l.container.Command = info.Command() } if onGCE { l.instance = &instanceInfo{ Zone: zone, Name: instanceName, ID: instanceID, } } else if info.Config[logZoneKey] != "" || info.Config[logNameKey] != "" || info.Config[logIDKey] != "" { l.instance = &instanceInfo{ Zone: info.Config[logZoneKey], Name: info.Config[logNameKey], ID: info.Config[logIDKey], } } // The logger "overflows" at a rate of 10,000 logs per second and this // overflow func is called. We want to surface the error to the user // without overly spamming /var/log/docker.log so we log the first time // we overflow and every 1000th time after. c.OnError = func(err error) { if err == logging.ErrOverflow { if i := atomic.AddUint64(&droppedLogs, 1); i%1000 == 1 { logrus.Errorf("gcplogs driver has dropped %v logs", i) } } else { logrus.Error(err) } } return l, nil }
func main() { launchConfig.MaybeDeploy() flag.Parse() setProdFlags() if *root == "" { var err error *root, err = os.Getwd() if err != nil { log.Fatalf("Failed to getwd: %v", err) } } readTemplates() if err := initGithubSyncing(); err != nil { log.Fatalf("error setting up syncing to github: %v") } go runDemoBlobserverLoop() mux := http.DefaultServeMux mux.Handle("/favicon.ico", http.FileServer(http.Dir(filepath.Join(*root, "static")))) mux.Handle("/robots.txt", http.FileServer(http.Dir(filepath.Join(*root, "static")))) mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(filepath.Join(*root, "static"))))) mux.Handle("/talks/", http.StripPrefix("/talks/", http.FileServer(http.Dir(filepath.Join(*root, "talks"))))) mux.Handle(pkgPattern, godocHandler{}) mux.Handle(cmdPattern, godocHandler{}) mux.Handle(appPattern, godocHandler{}) mux.HandleFunc(errPattern, errHandler) mux.HandleFunc("/r/", gerritRedirect) mux.HandleFunc("/dl/", releaseRedirect) mux.HandleFunc("/debug/ip", ipHandler) mux.HandleFunc("/debug/uptime", uptimeHandler) mux.Handle("/doc/contributing", redirTo("/code#contributing")) mux.Handle("/lists", redirTo("/community")) mux.HandleFunc("/contributors", contribHandler()) mux.HandleFunc("/doc/", docHandler) mux.HandleFunc("/", mainHandler) if buildbotHost != "" && buildbotBackend != "" { if _, err := url.Parse(buildbotBackend); err != nil { log.Fatalf("Failed to parse %v as a URL: %v", buildbotBackend, err) } bbhpattern := strings.TrimRight(buildbotHost, "/") + "/" mux.HandleFunc(bbhpattern, func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, buildbotBackend, http.StatusFound) }) } gceLauncher, err := gceDeployHandler("/launch/") if err != nil { log.Printf("Not installing GCE /launch/ handler: %v", err) mux.HandleFunc("/launch/", func(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("GCE launcher disabled: %v", err), 500) }) } else { mux.Handle("/launch/", gceLauncher) } var handler http.Handler = &redirectRootHandler{Handler: mux} if *logDir != "" || *logStdout { handler = NewLoggingHandler(handler, NewApacheLogger(*logDir, *logStdout)) } if *gceLogName != "" { projID := projectID() var hc *http.Client if !metadata.OnGCE() { hc = httpClient(projID) } ctx := context.Background() var logc *logging.Client if metadata.OnGCE() { logc, err = logging.NewClient(ctx, projID, *gceLogName) } else { logc, err = logging.NewClient(ctx, projID, *gceLogName, option.WithHTTPClient(hc)) } if err != nil { log.Fatal(err) } if err := logc.Ping(); err != nil { log.Fatalf("Failed to ping Google Cloud Logging: %v", err) } handler = NewLoggingHandler(handler, gceLogger{logc}) if gceLauncher != nil { var logc *logging.Client if metadata.OnGCE() { logc, err = logging.NewClient(ctx, projID, *gceLogName) } else { logc, err = logging.NewClient(ctx, projID, *gceLogName, option.WithHTTPClient(hc)) } if err != nil { log.Fatal(err) } logc.CommonLabels = map[string]string{ "from": "camli-gce-launcher", } logger := logc.Logger(logging.Default) logger.SetPrefix("launcher: ") gceLauncher.SetLogger(logger) } } emailErr := make(chan error) startEmailCommitLoop(emailErr) if *alsoRun != "" { runAsChild(*alsoRun) } httpServer := &http.Server{ Addr: *httpAddr, Handler: handler, ReadTimeout: 5 * time.Minute, WriteTimeout: 30 * time.Minute, } httpErr := make(chan error) go func() { log.Printf("Listening for HTTP on %v", *httpAddr) httpErr <- httpServer.ListenAndServe() }() httpsErr := make(chan error) if *httpsAddr != "" { go func() { httpsErr <- serveHTTPS(httpServer) }() } if *flagChromeBugRepro { go func() { log.Printf("Repro handler failed: %v", repro(":8001", "foo:bar")) }() } select { case err := <-emailErr: log.Fatalf("Error sending emails: %v", err) case err := <-httpErr: log.Fatalf("Error serving HTTP: %v", err) case err := <-httpsErr: log.Fatalf("Error serving HTTPS: %v", err) } }
func main() { flag.Parse() log.Printf("Starting server, os.Args=%s", strings.Join(os.Args, " ")) doc.SetDefaultGOOS(*defaultGOOS) httpClient = newHTTPClient() var ( gceLogName string projID string ) if metadata.OnGCE() { acct, err := metadata.ProjectAttributeValue("ga-account") if err != nil { log.Printf("querying metadata for ga-account: %v", err) } else { gaAccount = acct } // Get the log name on GCE and setup context for creating a GCE log client. if name, err := metadata.ProjectAttributeValue("gce-log-name"); err != nil { log.Printf("querying metadata for gce-log-name: %v", err) } else { gceLogName = name if id, err := metadata.ProjectID(); err != nil { log.Printf("querying metadata for project ID: %v", err) } else { projID = id } } } else { gaAccount = os.Getenv("GA_ACCOUNT") } if err := parseHTMLTemplates([][]string{ {"about.html", "common.html", "layout.html"}, {"bot.html", "common.html", "layout.html"}, {"cmd.html", "common.html", "layout.html"}, {"dir.html", "common.html", "layout.html"}, {"home.html", "common.html", "layout.html"}, {"importers.html", "common.html", "layout.html"}, {"importers_robot.html", "common.html", "layout.html"}, {"imports.html", "common.html", "layout.html"}, {"notfound.html", "common.html", "layout.html"}, {"pkg.html", "common.html", "layout.html"}, {"results.html", "common.html", "layout.html"}, {"tools.html", "common.html", "layout.html"}, {"std.html", "common.html", "layout.html"}, {"subrepo.html", "common.html", "layout.html"}, {"graph.html", "common.html"}, }); err != nil { log.Fatal(err) } if err := parseTextTemplates([][]string{ {"cmd.txt", "common.txt"}, {"dir.txt", "common.txt"}, {"home.txt", "common.txt"}, {"notfound.txt", "common.txt"}, {"pkg.txt", "common.txt"}, {"results.txt", "common.txt"}, }); err != nil { log.Fatal(err) } var err error db, err = database.New() if err != nil { log.Fatalf("Error opening database: %v", err) } go runBackgroundTasks() staticServer := httputil.StaticServer{ Dir: *assetsDir, MaxAge: time.Hour, MIMETypes: map[string]string{ ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8", }, } statusImageHandlerPNG = staticServer.FileHandler("status.png") statusImageHandlerSVG = staticServer.FileHandler("status.svg") apiMux := http.NewServeMux() apiMux.Handle("/favicon.ico", staticServer.FileHandler("favicon.ico")) apiMux.Handle("/google3d2f3cd4cc2bb44b.html", staticServer.FileHandler("google3d2f3cd4cc2bb44b.html")) apiMux.Handle("/humans.txt", staticServer.FileHandler("humans.txt")) apiMux.Handle("/robots.txt", staticServer.FileHandler("apiRobots.txt")) apiMux.Handle("/search", apiHandler(serveAPISearch)) apiMux.Handle("/packages", apiHandler(serveAPIPackages)) apiMux.Handle("/importers/", apiHandler(serveAPIImporters)) apiMux.Handle("/imports/", apiHandler(serveAPIImports)) apiMux.Handle("/", apiHandler(serveAPIHome)) mux := http.NewServeMux() mux.Handle("/-/site.js", staticServer.FilesHandler( "third_party/jquery.timeago.js", "site.js")) mux.Handle("/-/site.css", staticServer.FilesHandler("site.css")) mux.Handle("/-/bootstrap.min.css", staticServer.FilesHandler("bootstrap.min.css")) mux.Handle("/-/bootstrap.min.js", staticServer.FilesHandler("bootstrap.min.js")) mux.Handle("/-/jquery-2.0.3.min.js", staticServer.FilesHandler("jquery-2.0.3.min.js")) if *sidebarEnabled { mux.Handle("/-/sidebar.css", staticServer.FilesHandler("sidebar.css")) } mux.Handle("/-/", http.NotFoundHandler()) mux.Handle("/-/about", handler(serveAbout)) mux.Handle("/-/bot", handler(serveBot)) mux.Handle("/-/go", handler(serveGoIndex)) mux.Handle("/-/subrepo", handler(serveGoSubrepoIndex)) mux.Handle("/-/refresh", handler(serveRefresh)) mux.Handle("/-/admin/reindex", http.HandlerFunc(runReindex)) mux.Handle("/-/admin/purgeindex", http.HandlerFunc(runPurgeIndex)) mux.Handle("/about", http.RedirectHandler("/-/about", http.StatusMovedPermanently)) mux.Handle("/favicon.ico", staticServer.FileHandler("favicon.ico")) mux.Handle("/google3d2f3cd4cc2bb44b.html", staticServer.FileHandler("google3d2f3cd4cc2bb44b.html")) mux.Handle("/humans.txt", staticServer.FileHandler("humans.txt")) mux.Handle("/robots.txt", staticServer.FileHandler("robots.txt")) mux.Handle("/BingSiteAuth.xml", staticServer.FileHandler("BingSiteAuth.xml")) mux.Handle("/C", http.RedirectHandler("http://golang.org/doc/articles/c_go_cgo.html", http.StatusMovedPermanently)) mux.Handle("/code.jquery.com/", http.NotFoundHandler()) mux.Handle("/_ah/health", http.HandlerFunc(serveHealthCheck)) mux.Handle("/_ah/", http.NotFoundHandler()) mux.Handle("/", handler(serveHome)) cacheBusters.Handler = mux var root http.Handler = rootHandler{ {"api.", apiMux}, {"talks.godoc.org", otherDomainHandler{"https", "go-talks.appspot.com"}}, {"www.godoc.org", otherDomainHandler{"https", "godoc.org"}}, {"", mux}, } if gceLogName != "" { ctx := context.Background() logc, err := logging.NewClient(ctx, projID) if err != nil { log.Fatalf("Failed to create cloud logging client: %v", err) } logger := logc.Logger(gceLogName) if err := logc.Ping(ctx); err != nil { log.Fatalf("Failed to ping Google Cloud Logging: %v", err) } gceLogger = newGCELogger(logger) } http.Handle("/", root) appengine.Main() }
func TestMain(m *testing.M) { flag.Parse() // needed for testing.Short() ctx := context.Background() testProjectID = testutil.ProjID() errorc = make(chan error, 100) if testProjectID == "" || testing.Short() { integrationTest = false if testProjectID != "" { log.Print("Integration tests skipped in short mode (using fake instead)") } testProjectID = "PROJECT_ID" clean = func(e *logging.Entry) { // Remove the insert ID for consistency with the integration test. e.InsertID = "" } addr, err := ltesting.NewServer() if err != nil { log.Fatalf("creating fake server: %v", err) } logging.SetNow(testNow) newClients = func(ctx context.Context, projectID string) (*logging.Client, *logadmin.Client) { conn, err := grpc.Dial(addr, grpc.WithInsecure()) if err != nil { log.Fatalf("dialing %q: %v", addr, err) } c, err := logging.NewClient(ctx, projectID, option.WithGRPCConn(conn)) if err != nil { log.Fatalf("creating client for fake at %q: %v", addr, err) } ac, err := logadmin.NewClient(ctx, projectID, option.WithGRPCConn(conn)) if err != nil { log.Fatalf("creating client for fake at %q: %v", addr, err) } return c, ac } } else { integrationTest = true clean = func(e *logging.Entry) { // We cannot compare timestamps, so set them to the test time. // Also, remove the insert ID added by the service. e.Timestamp = testNow().UTC() e.InsertID = "" } ts := testutil.TokenSource(ctx, logging.AdminScope) if ts == nil { log.Fatal("The project key must be set. See CONTRIBUTING.md for details") } log.Printf("running integration tests with project %s", testProjectID) newClients = func(ctx context.Context, projectID string) (*logging.Client, *logadmin.Client) { c, err := logging.NewClient(ctx, projectID, option.WithTokenSource(ts)) if err != nil { log.Fatalf("creating prod client: %v", err) } ac, err := logadmin.NewClient(ctx, projectID, option.WithTokenSource(ts)) if err != nil { log.Fatalf("creating prod client: %v", err) } return c, ac } } client, aclient = newClients(ctx, testProjectID) client.OnError = func(e error) { errorc <- e } initLogs(ctx) testFilter = fmt.Sprintf(`logName = "projects/%s/logs/%s"`, testProjectID, strings.Replace(testLogID, "/", "%2F", -1)) exit := m.Run() client.Close() os.Exit(exit) }
type apiInterface interface { ReportErrorEvent(ctx context.Context, req *erpb.ReportErrorEventRequest) (*erpb.ReportErrorEventResponse, error) } var newApiInterface = func(ctx context.Context, opts ...option.ClientOption) (apiInterface, error) { client, err := api.NewReportErrorsClient(ctx, opts...) return client, err } type loggerInterface interface { LogSync(ctx context.Context, e logging.Entry) error } var newLoggerInterface = func(ctx context.Context, projectID string, opts ...option.ClientOption) (loggerInterface, error) { lc, err := logging.NewClient(ctx, projectID, opts...) if err != nil { return nil, fmt.Errorf("creating Logging client: %v", err) } l := lc.Logger("errorreports") return l, nil } type sender interface { send(ctx context.Context, r *http.Request, message string) } // errorApiSender sends error reports using the Stackdriver Error Reporting API. type errorApiSender struct { apiClient apiInterface projectID string
func main() { flag.Parse() t := time.Now() expvar.NewInt("start_time_epoch_secs").Set(t.Unix()) expvar.NewString("start_time_timestamp").Set(t.Format(time.RFC3339)) expvar.Publish("uptime_secs", expvar.Func(func() interface{} { return int64(time.Now().Sub(t) / time.Second) })) expvar.Publish("uptime_dur", expvar.Func(func() interface{} { return time.Now().Sub(t).String() })) routeHost, redirectHost := calculateDomains(*rawVHost, *httpsAddr) apiVars.Set("requests", apiRequests) staticVars.Set("requests", staticRequests) webVars.Set("requests", webRequests) tlsConf := makeTLSConfig(*certPath, *keyPath) tlsListener, err := tls.Listen("tcp", *httpsAddr, tlsConf) if err != nil { log.Fatalf("unable to listen for the HTTPS server on %s: %s", *httpsAddr, err) } plaintextListener, err := net.Listen("tcp", *httpAddr) if err != nil { log.Fatalf("unable to listen for the HTTP server on %s: %s", *httpAddr, err) } ns := expvar.NewMap("tls") l := newListener(tlsListener, ns) if *acmeURL != "" { if !strings.HasPrefix(*acmeURL, "/") && !strings.HasPrefix(*acmeURL, "https://") && !strings.HasPrefix(*acmeURL, "http://") { fmt.Fprintf(os.Stderr, "acmeRedirect must start with 'http://', 'https://', or '/' but does not: %#v\n", *acmeURL) os.Exit(1) } } blockedOrigins := []string{} if *originsFile != "" { jc := loadOriginsConfig(*originsFile) blockedOrigins = jc.BlockedOrigins } hostname, err := os.Hostname() if err != nil { log.Fatalf("unable to get hostname of local machine: %s", err) } var gclog logClient if *googAcctConf != "" { googConf := loadGoogleServiceAccount(*googAcctConf) tokSrc := googConf.conf.TokenSource(context.Background()) client, err := logging.NewClient(context.Background(), googConf.ProjectID, option.WithTokenSource(tokSrc)) if err != nil { log.Fatalf("unable to make Google Cloud Logging client: %s", err) } client.OnError = func(err error) { log.Printf("goog logging error: %s", err) } gclog = client.Logger(*allowLogName) } else { gclog = nullLogClient{} } oa := newOriginAllower(blockedOrigins, hostname, gclog, expvar.NewMap("origins")) staticHandler := http.NotFoundHandler() webHandleFunc := http.NotFound if !*headless { index = loadIndex() staticHandler = makeStaticHandler(*staticDir, staticVars) webHandleFunc = handleWeb } m := tlsMux( routeHost, redirectHost, *acmeURL, staticHandler, webHandleFunc, oa, ) go func() { err := http.ListenAndServe(*adminAddr, nil) if err != nil { log.Fatalf("unable to open admin server: %s", err) } }() httpsSrv := &http.Server{ Handler: m, ReadTimeout: 10 * time.Second, WriteTimeout: 15 * time.Second, } httpSrv := &http.Server{ Handler: plaintextMux(redirectHost), ReadTimeout: 10 * time.Second, WriteTimeout: 15 * time.Second, } log.Printf("Booting HTTPS on %s and HTTP on %s", *httpsAddr, *httpAddr) go func() { err := httpsSrv.Serve(l) if err != nil { log.Fatalf("https server error: %s", err) } }() err = httpSrv.Serve(plaintextListener) if err != nil { log.Fatalf("http server error: %s", err) } }