github.com/ethereum-optimism/optimism@v1.7.2/op-node/heartbeat/service.go (about)

     1  // Package heartbeat provides a service for sending heartbeats to a server.
     2  package heartbeat
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/log"
    13  )
    14  
    15  // SendInterval determines the delay between requests. This must be larger than the MinHeartbeatInterval in the server.
    16  const SendInterval = 10 * time.Minute
    17  
    18  type Payload struct {
    19  	Version string `json:"version"`
    20  	Meta    string `json:"meta"`
    21  	Moniker string `json:"moniker"`
    22  	PeerID  string `json:"peerID"`
    23  	ChainID uint64 `json:"chainID"`
    24  }
    25  
    26  // Beat sends a heartbeat to the server at the given URL. It will send a heartbeat immediately, and then every SendInterval.
    27  // Beat spawns a goroutine that will send heartbeats until the context is canceled.
    28  func Beat(
    29  	ctx context.Context,
    30  	log log.Logger,
    31  	url string,
    32  	payload *Payload,
    33  ) error {
    34  	payloadJSON, err := json.Marshal(payload)
    35  	if err != nil {
    36  		return fmt.Errorf("telemetry crashed: %w", err)
    37  	}
    38  
    39  	client := &http.Client{
    40  		Timeout: 10 * time.Second,
    41  	}
    42  
    43  	send := func() {
    44  		req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(payloadJSON))
    45  		req.Header.Set("User-Agent", fmt.Sprintf("op-node/%s", payload.Version))
    46  		req.Header.Set("Content-Type", "application/json")
    47  		if err != nil {
    48  			log.Error("error creating heartbeat HTTP request", "err", err)
    49  			return
    50  		}
    51  		res, err := client.Do(req)
    52  		if err != nil {
    53  			log.Warn("error sending heartbeat", "err", err)
    54  			return
    55  		}
    56  		res.Body.Close()
    57  
    58  		if res.StatusCode < 200 || res.StatusCode > 204 {
    59  			log.Warn("heartbeat server returned non-200 status code", "status", res.StatusCode)
    60  			return
    61  		}
    62  
    63  		log.Info("sent heartbeat")
    64  	}
    65  
    66  	send()
    67  	tick := time.NewTicker(SendInterval)
    68  	defer tick.Stop()
    69  	for {
    70  		select {
    71  		case <-tick.C:
    72  			send()
    73  		case <-ctx.Done():
    74  			return nil
    75  		}
    76  	}
    77  }