@@ -236,9 +236,16 @@ func (b *Backend) List(ctx context.Context, prefix string) (blobList simpleblob.
236236 return blobs .WithPrefix (prefix ), nil
237237}
238238
239+ func recordMinioDurationMetric (method string , start time.Time ) {
240+ elapsed := time .Since (start )
241+ metricCallHistogram .WithLabelValues (method ).Observe (elapsed .Seconds ())
242+ }
243+
239244func (b * Backend ) doList (ctx context.Context , prefix string ) (simpleblob.BlobList , error ) {
240245 var blobs simpleblob.BlobList
241246
247+ defer recordMinioDurationMetric ("list" , time .Now ())
248+
242249 // Runes to strip from blob names for GlobalPrefix
243250 // This is fine, because we can trust the API to only return with the prefix.
244251 // TODO: trust but verify
@@ -252,6 +259,7 @@ func (b *Backend) doList(ctx context.Context, prefix string) (simpleblob.BlobLis
252259 // Handle error returned by MinIO client
253260 if err := convertMinioError (obj .Err , true ); err != nil {
254261 metricCallErrors .WithLabelValues ("list" ).Inc ()
262+ metricCallErrorsType .WithLabelValues ("list" , errorToMetricsLabel (err )).Inc ()
255263 return nil , err
256264 }
257265
@@ -303,9 +311,12 @@ func (b *Backend) doLoadReader(ctx context.Context, name string) (io.ReadCloser,
303311 metricCalls .WithLabelValues ("load" ).Inc ()
304312 metricLastCallTimestamp .WithLabelValues ("load" ).SetToCurrentTime ()
305313
314+ defer recordMinioDurationMetric ("load" , time .Now ())
315+
306316 obj , err := b .client .GetObject (ctx , b .opt .Bucket , name , minio.GetObjectOptions {})
307317 if err = convertMinioError (err , false ); err != nil {
308318 metricCallErrors .WithLabelValues ("load" ).Inc ()
319+ metricCallErrorsType .WithLabelValues ("load" , errorToMetricsLabel (err )).Inc ()
309320 return nil , err
310321 }
311322 if obj == nil {
@@ -314,6 +325,7 @@ func (b *Backend) doLoadReader(ctx context.Context, name string) (io.ReadCloser,
314325 info , err := obj .Stat ()
315326 if err = convertMinioError (err , false ); err != nil {
316327 metricCallErrors .WithLabelValues ("load" ).Inc ()
328+ metricCallErrorsType .WithLabelValues ("load" , errorToMetricsLabel (err )).Inc ()
317329 return nil , err
318330 }
319331 if info .Key == "" {
@@ -347,6 +359,7 @@ func (b *Backend) doStore(ctx context.Context, name string, data []byte) (minio.
347359func (b * Backend ) doStoreReader (ctx context.Context , name string , r io.Reader , size int64 ) (minio.UploadInfo , error ) {
348360 metricCalls .WithLabelValues ("store" ).Inc ()
349361 metricLastCallTimestamp .WithLabelValues ("store" ).SetToCurrentTime ()
362+ defer recordMinioDurationMetric ("store" , time .Now ())
350363
351364 putObjectOptions := minio.PutObjectOptions {
352365 NumThreads : b .opt .NumMinioThreads ,
@@ -358,6 +371,7 @@ func (b *Backend) doStoreReader(ctx context.Context, name string, r io.Reader, s
358371 err = convertMinioError (err , false )
359372 if err != nil {
360373 metricCallErrors .WithLabelValues ("store" ).Inc ()
374+ metricCallErrorsType .WithLabelValues ("store" , errorToMetricsLabel (err )).Inc ()
361375 }
362376 return info , err
363377}
@@ -377,10 +391,12 @@ func (b *Backend) Delete(ctx context.Context, name string) error {
377391func (b * Backend ) doDelete (ctx context.Context , name string ) error {
378392 metricCalls .WithLabelValues ("delete" ).Inc ()
379393 metricLastCallTimestamp .WithLabelValues ("delete" ).SetToCurrentTime ()
394+ defer recordMinioDurationMetric ("delete" , time .Now ())
380395
381396 err := b .client .RemoveObject (ctx , b .opt .Bucket , name , minio.RemoveObjectOptions {})
382397 if err = convertMinioError (err , false ); err != nil {
383398 metricCallErrors .WithLabelValues ("delete" ).Inc ()
399+ metricCallErrorsType .WithLabelValues ("delete" , errorToMetricsLabel (err )).Inc ()
384400 }
385401 return err
386402}
@@ -555,6 +571,40 @@ func convertMinioError(err error, isList bool) error {
555571 return err
556572}
557573
574+ // errorToMetricsLabel converts an error into a prometheus label.
575+ // If error is a NotExist error, "NotFound" is returned.
576+ // If error is a timeout, "Timeout" is returned.
577+ // If error is a DNS error, the DNS error is returned.
578+ // If error is a URL error, the URL error is returned.
579+ // If error is a MinIO error, the MinIO error code is returned.
580+ // Otherwise "Unknown" is returned.
581+ func errorToMetricsLabel (err error ) string {
582+ if err == nil {
583+ return "ok"
584+ }
585+ if errors .Is (err , os .ErrNotExist ) {
586+ return "NotFound"
587+ }
588+ var netError * net.OpError
589+ if errors .Is (err , context .DeadlineExceeded ) ||
590+ (errors .As (err , & netError ) && netError .Timeout ()) {
591+ return "Timeout"
592+ }
593+ var dnsErr * net.DNSError
594+ if errors .As (err , & dnsErr ) {
595+ return "DNSError"
596+ }
597+ var urlErr * url.Error
598+ if errors .As (err , & urlErr ) {
599+ return "URLError"
600+ }
601+ errRes := minio .ToErrorResponse (err )
602+ if errRes .Code != "" {
603+ return errRes .Code
604+ }
605+ return "Unknown"
606+ }
607+
558608func getOpt [T comparable ](optVal , defaultVal T ) T {
559609 var zero T
560610 if optVal == zero {
0 commit comments