github.com/opensearch-project/opensearch-go/v2@v2.3.0/opensearchtransport/metrics.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  //
     3  // The OpenSearch Contributors require contributions made to
     4  // this file be licensed under the Apache-2.0 license or a
     5  // compatible open source license.
     6  //
     7  // Modifications Copyright OpenSearch Contributors. See
     8  // GitHub history for details.
     9  
    10  // Licensed to Elasticsearch B.V. under one or more contributor
    11  // license agreements. See the NOTICE file distributed with
    12  // this work for additional information regarding copyright
    13  // ownership. Elasticsearch B.V. licenses this file to you under
    14  // the Apache License, Version 2.0 (the "License"); you may
    15  // not use this file except in compliance with the License.
    16  // You may obtain a copy of the License at
    17  //
    18  //    http://www.apache.org/licenses/LICENSE-2.0
    19  //
    20  // Unless required by applicable law or agreed to in writing,
    21  // software distributed under the License is distributed on an
    22  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    23  // KIND, either express or implied.  See the License for the
    24  // specific language governing permissions and limitations
    25  // under the License.
    26  
    27  package opensearchtransport
    28  
    29  import (
    30  	"errors"
    31  	"fmt"
    32  	"strconv"
    33  	"strings"
    34  	"sync"
    35  	"time"
    36  )
    37  
    38  // Measurable defines the interface for transports supporting metrics.
    39  //
    40  type Measurable interface {
    41  	Metrics() (Metrics, error)
    42  }
    43  
    44  // connectionable defines the interface for transports returning a list of connections.
    45  //
    46  type connectionable interface {
    47  	connections() []*Connection
    48  }
    49  
    50  // Metrics represents the transport metrics.
    51  //
    52  type Metrics struct {
    53  	Requests  int         `json:"requests"`
    54  	Failures  int         `json:"failures"`
    55  	Responses map[int]int `json:"responses"`
    56  
    57  	Connections []fmt.Stringer `json:"connections"`
    58  }
    59  
    60  // ConnectionMetric represents metric information for a connection.
    61  //
    62  type ConnectionMetric struct {
    63  	URL       string     `json:"url"`
    64  	Failures  int        `json:"failures,omitempty"`
    65  	IsDead    bool       `json:"dead,omitempty"`
    66  	DeadSince *time.Time `json:"dead_since,omitempty"`
    67  
    68  	Meta struct {
    69  		ID    string   `json:"id"`
    70  		Name  string   `json:"name"`
    71  		Roles []string `json:"roles"`
    72  	} `json:"meta"`
    73  }
    74  
    75  // metrics represents the inner state of metrics.
    76  //
    77  type metrics struct {
    78  	sync.RWMutex
    79  
    80  	requests  int
    81  	failures  int
    82  	responses map[int]int
    83  
    84  	connections []*Connection
    85  }
    86  
    87  // Metrics returns the transport metrics.
    88  //
    89  func (c *Client) Metrics() (Metrics, error) {
    90  	if c.metrics == nil {
    91  		return Metrics{}, errors.New("transport metrics not enabled")
    92  	}
    93  	c.metrics.RLock()
    94  	defer c.metrics.RUnlock()
    95  
    96  	if lockable, ok := c.pool.(sync.Locker); ok {
    97  		lockable.Lock()
    98  		defer lockable.Unlock()
    99  	}
   100  
   101  	m := Metrics{
   102  		Requests:  c.metrics.requests,
   103  		Failures:  c.metrics.failures,
   104  		Responses: c.metrics.responses,
   105  	}
   106  
   107  	if pool, ok := c.pool.(connectionable); ok {
   108  		for _, c := range pool.connections() {
   109  			c.Lock()
   110  
   111  			cm := ConnectionMetric{
   112  				URL:      c.URL.String(),
   113  				IsDead:   c.IsDead,
   114  				Failures: c.Failures,
   115  			}
   116  
   117  			if !c.DeadSince.IsZero() {
   118  				cm.DeadSince = &c.DeadSince
   119  			}
   120  
   121  			if c.ID != "" {
   122  				cm.Meta.ID = c.ID
   123  			}
   124  
   125  			if c.Name != "" {
   126  				cm.Meta.Name = c.Name
   127  			}
   128  
   129  			if len(c.Roles) > 0 {
   130  				cm.Meta.Roles = c.Roles
   131  			}
   132  
   133  			m.Connections = append(m.Connections, cm)
   134  			c.Unlock()
   135  		}
   136  	}
   137  
   138  	return m, nil
   139  }
   140  
   141  // String returns the metrics as a string.
   142  //
   143  func (m Metrics) String() string {
   144  	var (
   145  		i int
   146  		b strings.Builder
   147  	)
   148  	b.WriteString("{")
   149  
   150  	b.WriteString("Requests:")
   151  	b.WriteString(strconv.Itoa(m.Requests))
   152  
   153  	b.WriteString(" Failures:")
   154  	b.WriteString(strconv.Itoa(m.Failures))
   155  
   156  	if len(m.Responses) > 0 {
   157  		b.WriteString(" Responses: ")
   158  		b.WriteString("[")
   159  
   160  		for code, num := range m.Responses {
   161  			b.WriteString(strconv.Itoa(code))
   162  			b.WriteString(":")
   163  			b.WriteString(strconv.Itoa(num))
   164  			if i+1 < len(m.Responses) {
   165  				b.WriteString(", ")
   166  			}
   167  			i++
   168  		}
   169  		b.WriteString("]")
   170  	}
   171  
   172  	b.WriteString(" Connections: [")
   173  	for i, c := range m.Connections {
   174  		b.WriteString(c.String())
   175  		if i+1 < len(m.Connections) {
   176  			b.WriteString(", ")
   177  		}
   178  		i++
   179  	}
   180  	b.WriteString("]")
   181  
   182  	b.WriteString("}")
   183  	return b.String()
   184  }
   185  
   186  // String returns the connection information as a string.
   187  //
   188  func (cm ConnectionMetric) String() string {
   189  	var b strings.Builder
   190  	b.WriteString("{")
   191  	b.WriteString(cm.URL)
   192  	if cm.IsDead {
   193  		fmt.Fprintf(&b, " dead=%v", cm.IsDead)
   194  	}
   195  	if cm.Failures > 0 {
   196  		fmt.Fprintf(&b, " failures=%d", cm.Failures)
   197  	}
   198  	if cm.DeadSince != nil {
   199  		fmt.Fprintf(&b, " dead_since=%s", cm.DeadSince.Local().Format(time.Stamp))
   200  	}
   201  	b.WriteString("}")
   202  	return b.String()
   203  }