core/log/metric/common.go (94 lines of code) (raw):

// Copyright 1999-2020 Alibaba Group Holding Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric import ( "io/ioutil" "os" "path/filepath" "regexp" "sort" "strconv" "strings" "github.com/alibaba/sentinel-golang/core/base" ) const ( // MetricFileNameSuffix represents the suffix of the metric file. MetricFileNameSuffix = "metrics.log" // MetricIdxSuffix represents the suffix of the metric index file. MetricIdxSuffix = ".idx" // FileLockSuffix represents the suffix of the lock file. FileLockSuffix = ".lck" // FilePidPrefix represents the pid flag of filename. FilePidPrefix = "pid" metricFilePattern = `\.[0-9]{4}-[0-9]{2}-[0-9]{2}(\.[0-9]*)?` ) var metricFileRegex = regexp.MustCompile(metricFilePattern) // MetricLogWriter writes and flushes metric items to current metric log. type MetricLogWriter interface { Write(ts uint64, items []*base.MetricItem) error } // MetricSearcher searches metric items from the metric log file under given condition. type MetricSearcher interface { FindByTimeAndResource(beginTimeMs uint64, endTimeMs uint64, resource string) ([]*base.MetricItem, error) FindFromTimeWithMaxLines(beginTimeMs uint64, maxLines uint32) ([]*base.MetricItem, error) } // Generate the metric file name from the service name. func FormMetricFileName(serviceName string, withPid bool) string { dot := "." separator := "-" if strings.Contains(serviceName, dot) { serviceName = strings.ReplaceAll(serviceName, dot, separator) } filename := serviceName + separator + MetricFileNameSuffix if withPid { pid := os.Getpid() filename = filename + ".pid" + strconv.Itoa(pid) } return filename } // Generate the metric index filename from the metric log filename. func formMetricIdxFileName(metricFilename string) string { return metricFilename + MetricIdxSuffix } func filenameMatches(filename, baseFilename string) bool { if !strings.HasPrefix(filename, baseFilename) { return false } part := filename[len(baseFilename):] // part is like: ".yyyy-MM-dd.number", eg. ".2018-12-24.11" return metricFileRegex.MatchString(part) } func listMetricFilesConditional(baseDir string, filePattern string, predicate func(string, string) bool) ([]string, error) { dir, err := ioutil.ReadDir(baseDir) if err != nil { return nil, err } arr := make([]string, 0, len(dir)) for _, f := range dir { if f.IsDir() { continue } name := f.Name() if predicate(name, filePattern) && !strings.HasSuffix(name, MetricIdxSuffix) && !strings.HasSuffix(name, FileLockSuffix) { // Put the absolute path into the slice. arr = append(arr, filepath.Join(baseDir, name)) } } if len(arr) > 1 { sort.Slice(arr, filenameComparator(arr)) } return arr, nil } // List metrics files // baseDir: the directory of metrics files // filePattern: metric file pattern func listMetricFiles(baseDir, filePattern string) ([]string, error) { return listMetricFilesConditional(baseDir, filePattern, filenameMatches) } func filenameComparator(arr []string) func(i, j int) bool { return func(i, j int) bool { name1 := filepath.Base(arr[i]) name2 := filepath.Base(arr[j]) a1 := strings.Split(name1, `.`) a2 := strings.Split(name2, `.`) dateStr1 := a1[2] dateStr2 := a2[2] // in case of file name contains pid, skip it, like Sentinel-Admin-metrics.log.pid22568.2018-12-24 if strings.HasPrefix(a1[2], FilePidPrefix) { dateStr1 = a1[3] dateStr2 = a2[3] } // compare date first if dateStr1 != dateStr2 { return dateStr1 < dateStr2 } // same date, compare the file number t := len(name1) - len(name2) if t != 0 { return t < 0 } return name1 < name2 } }