mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 22:39:01 +08:00
Merge branch 'develop' into feat/opamp-logparing
This commit is contained in:
commit
55d7285c9a
35
README.md
35
README.md
@ -35,14 +35,31 @@ SigNoz helps developers monitor applications and troubleshoot problems in their
|
||||
|
||||
👉 Filter and query logs, build dashboards and alerts based on attributes in logs
|
||||
|
||||

|
||||
<br />
|
||||

|
||||
<br />
|
||||

|
||||
<br />
|
||||

|
||||
👉 Record exceptions automatically in Python, Java, Ruby, and Javascript
|
||||
|
||||
👉 Easy to set alerts with DIY query builder
|
||||
|
||||
|
||||
### Application Metrics
|
||||
|
||||
<img width="2560" alt="application_metrics" src="https://user-images.githubusercontent.com/83692067/226536399-be9298c9-b4c1-4879-83b9-bc73417c032c.png">
|
||||
|
||||
### Distributed Tracing
|
||||
<img width="2068" alt="distributed_tracing_2 2" src="https://user-images.githubusercontent.com/83692067/226536447-bae58321-6a22-4ed3-af80-e3e964cb3489.png">
|
||||
|
||||
<img width="2068" alt="distributed_tracing_1" src="https://user-images.githubusercontent.com/83692067/226536462-939745b6-4f9d-45a6-8016-814837e7f7b4.png">
|
||||
|
||||
### Logs Management
|
||||
|
||||
<img width="2068" alt="logs_management" src="https://user-images.githubusercontent.com/83692067/226536482-b8a5c4af-b69c-43d5-969c-338bd5eaf1a5.png">
|
||||
|
||||
### Infrastructure Monitoring
|
||||
|
||||
<img width="2068" alt="infrastructure_monitoring" src="https://user-images.githubusercontent.com/83692067/226536496-f38c4dbf-e03c-4158-8be0-32d4a61158c7.png">
|
||||
|
||||
### Alerts
|
||||
|
||||
<img width="2068" alt="alerts_management" src="https://user-images.githubusercontent.com/83692067/226536548-2c81e2e8-c12d-47e8-bad7-c6be79055def.png">
|
||||
|
||||
|
||||
<br /><br />
|
||||
@ -65,6 +82,10 @@ Come say Hi to us on [Slack](https://signoz.io/slack) 👋
|
||||
- See exact request trace to figure out issues in downstream services, slow DB queries, call to 3rd party services like payment gateways, etc
|
||||
- Filter traces by service name, operation, latency, error, tags/annotations.
|
||||
- Run aggregates on trace data (events/spans) to get business relevant metrics. e.g. You can get error rate and 99th percentile latency of `customer_type: gold` or `deployment_version: v2` or `external_call: paypal`
|
||||
- Native support for OpenTelemetry Logs, advanced log query builder, and automatic log collection from k8s cluster
|
||||
- Lightening quick log analytics ([Logs Perf. Benchmark](https://signoz.io/blog/logs-performance-benchmark/))
|
||||
- End-to-End visibility into infrastructure performance, ingest metrics from all kinds of host environments
|
||||
- Easy to set alerts with DIY query builder
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
@ -32,8 +32,9 @@ function TimePreference({
|
||||
|
||||
return (
|
||||
<TextContainer noButtonMargin>
|
||||
<Dropdown menu={menu} />
|
||||
<Button>{selectedTime.name}</Button>
|
||||
<Dropdown menu={menu}>
|
||||
<Button>{selectedTime.name}</Button>
|
||||
</Dropdown>
|
||||
</TextContainer>
|
||||
);
|
||||
}
|
||||
|
@ -44,6 +44,9 @@ var AggregateOperatorToSQLFunc = map[model.AggregateOperator]string{
|
||||
model.RATE_MIN: "min",
|
||||
}
|
||||
|
||||
// See https://github.com/SigNoz/signoz/issues/2151#issuecomment-1467249056
|
||||
var rateWithoutNegative = `if (runningDifference(value) < 0 OR runningDifference(ts) < 0, nan, runningDifference(value)/runningDifference(ts))`
|
||||
|
||||
var SupportedFunctions = []string{"exp", "log", "ln", "exp2", "log2", "exp10", "log10", "sqrt", "cbrt", "erf", "erfc", "lgamma", "tgamma", "sin", "cos", "tan", "asin", "acos", "atan", "degrees", "radians"}
|
||||
|
||||
func GoValuateFuncs() map[string]govaluate.ExpressionFunction {
|
||||
@ -200,7 +203,7 @@ func BuildMetricQuery(qp *model.QueryRangeParamsV2, mq *model.MetricQuery, table
|
||||
subQuery := fmt.Sprintf(
|
||||
queryTmpl, "any(labels) as labels, "+groupTags, qp.Step, op, filterSubQuery, groupBy, groupTags,
|
||||
) // labels will be same so any should be fine
|
||||
query := `SELECT %s ts, runningDifference(value)/runningDifference(ts) as value FROM(%s)`
|
||||
query := `SELECT %s ts, ` + rateWithoutNegative + ` as value FROM(%s)`
|
||||
|
||||
query = fmt.Sprintf(query, "labels as fullLabels,", subQuery)
|
||||
return query, nil
|
||||
@ -211,14 +214,14 @@ func BuildMetricQuery(qp *model.QueryRangeParamsV2, mq *model.MetricQuery, table
|
||||
subQuery := fmt.Sprintf(
|
||||
queryTmpl, rateGroupTags, qp.Step, op, filterSubQuery, rateGroupBy, rateGroupTags,
|
||||
) // labels will be same so any should be fine
|
||||
query := `SELECT %s ts, runningDifference(value)/runningDifference(ts) as value FROM(%s) OFFSET 1`
|
||||
query := `SELECT %s ts, ` + rateWithoutNegative + `as value FROM(%s)`
|
||||
query = fmt.Sprintf(query, groupTags, subQuery)
|
||||
query = fmt.Sprintf(`SELECT %s ts, sum(value) as value FROM (%s) GROUP BY %s ORDER BY %s ts`, groupTags, query, groupBy, groupTags)
|
||||
return query, nil
|
||||
case model.RATE_SUM, model.RATE_MAX, model.RATE_AVG, model.RATE_MIN:
|
||||
op := fmt.Sprintf("%s(value)", AggregateOperatorToSQLFunc[mq.AggregateOperator])
|
||||
subQuery := fmt.Sprintf(queryTmpl, groupTags, qp.Step, op, filterSubQuery, groupBy, groupTags)
|
||||
query := `SELECT %s ts, runningDifference(value)/runningDifference(ts) as value FROM(%s) OFFSET 1`
|
||||
query := `SELECT %s ts, ` + rateWithoutNegative + `as value FROM(%s)`
|
||||
query = fmt.Sprintf(query, groupTags, subQuery)
|
||||
return query, nil
|
||||
case model.P05, model.P10, model.P20, model.P25, model.P50, model.P75, model.P90, model.P95, model.P99:
|
||||
@ -232,9 +235,10 @@ func BuildMetricQuery(qp *model.QueryRangeParamsV2, mq *model.MetricQuery, table
|
||||
subQuery := fmt.Sprintf(
|
||||
queryTmpl, rateGroupTags, qp.Step, op, filterSubQuery, rateGroupBy, rateGroupTags,
|
||||
) // labels will be same so any should be fine
|
||||
query := `SELECT %s ts, runningDifference(value)/runningDifference(ts) as value FROM(%s) OFFSET 1`
|
||||
query := `SELECT %s ts, ` + rateWithoutNegative + ` as value FROM(%s)`
|
||||
query = fmt.Sprintf(query, groupTags, subQuery)
|
||||
query = fmt.Sprintf(`SELECT %s ts, sum(value) as value FROM (%s) GROUP BY %s ORDER BY %s ts`, groupTags, query, groupBy, groupTags)
|
||||
// filter out NaN values from the rate query as histogramQuantile doesn't support NaN values
|
||||
query = fmt.Sprintf(`SELECT %s ts, sum(value) as value FROM (%s) GROUP BY %s HAVING isNaN(value) = 0 ORDER BY %s ts`, groupTags, query, groupBy, groupTags)
|
||||
value := AggregateOperatorToPercentile[mq.AggregateOperator]
|
||||
|
||||
query = fmt.Sprintf(`SELECT %s ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), %.3f) as value FROM (%s) GROUP BY %s ORDER BY %s ts`, groupTagsWithoutLe, value, query, groupByWithoutLe, groupTagsWithoutLe)
|
||||
|
@ -28,7 +28,30 @@ func TestBuildQuery(t *testing.T) {
|
||||
queries := PrepareBuilderMetricQueries(q, "table").Queries
|
||||
So(len(queries), ShouldEqual, 1)
|
||||
So(queries["A"], ShouldContainSubstring, "WHERE metric_name = 'name'")
|
||||
So(queries["A"], ShouldContainSubstring, "runningDifference(value)/runningDifference(ts)")
|
||||
So(queries["A"], ShouldContainSubstring, rateWithoutNegative)
|
||||
})
|
||||
|
||||
Convey("TestSimpleQueryWithHistQuantile", t, func() {
|
||||
q := &model.QueryRangeParamsV2{
|
||||
Start: 1650991982000,
|
||||
End: 1651078382000,
|
||||
Step: 60,
|
||||
CompositeMetricQuery: &model.CompositeMetricQuery{
|
||||
BuilderQueries: map[string]*model.MetricQuery{
|
||||
"A": {
|
||||
QueryName: "A",
|
||||
MetricName: "name",
|
||||
AggregateOperator: model.HIST_QUANTILE_99,
|
||||
Expression: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
queries := PrepareBuilderMetricQueries(q, "table").Queries
|
||||
So(len(queries), ShouldEqual, 1)
|
||||
So(queries["A"], ShouldContainSubstring, "WHERE metric_name = 'name'")
|
||||
So(queries["A"], ShouldContainSubstring, rateWithoutNegative)
|
||||
So(queries["A"], ShouldContainSubstring, "HAVING isNaN(value) = 0")
|
||||
})
|
||||
}
|
||||
|
||||
@ -57,7 +80,7 @@ func TestBuildQueryWithFilters(t *testing.T) {
|
||||
So(len(queries), ShouldEqual, 1)
|
||||
|
||||
So(queries["A"], ShouldContainSubstring, "WHERE metric_name = 'name' AND JSONExtractString(labels, 'a') != 'b'")
|
||||
So(queries["A"], ShouldContainSubstring, "runningDifference(value)/runningDifference(ts)")
|
||||
So(queries["A"], ShouldContainSubstring, rateWithoutNegative)
|
||||
So(queries["A"], ShouldContainSubstring, "not match(JSONExtractString(labels, 'code'), 'ERROR_*')")
|
||||
})
|
||||
}
|
||||
@ -91,7 +114,7 @@ func TestBuildQueryWithMultipleQueries(t *testing.T) {
|
||||
queries := PrepareBuilderMetricQueries(q, "table").Queries
|
||||
So(len(queries), ShouldEqual, 2)
|
||||
So(queries["A"], ShouldContainSubstring, "WHERE metric_name = 'name' AND JSONExtractString(labels, 'in') IN ['a','b','c']")
|
||||
So(queries["A"], ShouldContainSubstring, "runningDifference(value)/runningDifference(ts)")
|
||||
So(queries["A"], ShouldContainSubstring, rateWithoutNegative)
|
||||
})
|
||||
}
|
||||
|
||||
@ -128,7 +151,7 @@ func TestBuildQueryWithMultipleQueriesAndFormula(t *testing.T) {
|
||||
So(len(queries), ShouldEqual, 3)
|
||||
So(queries["C"], ShouldContainSubstring, "SELECT A.ts as ts, A.value / B.value")
|
||||
So(queries["C"], ShouldContainSubstring, "WHERE metric_name = 'name' AND JSONExtractString(labels, 'in') IN ['a','b','c']")
|
||||
So(queries["C"], ShouldContainSubstring, "runningDifference(value)/runningDifference(ts)")
|
||||
So(queries["C"], ShouldContainSubstring, rateWithoutNegative)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,6 @@ type FilterAttributeKeyResponse struct {
|
||||
type AttributeKeyType string
|
||||
|
||||
const (
|
||||
AttributeKeyTypeColumn AttributeKeyType = "column"
|
||||
AttributeKeyTypeTag AttributeKeyType = "tag"
|
||||
AttributeKeyTypeResource AttributeKeyType = "resource"
|
||||
)
|
||||
@ -238,6 +237,29 @@ type AttributeKey struct {
|
||||
Key string `json:"key"`
|
||||
DataType AttributeKeyDataType `json:"dataType"`
|
||||
Type AttributeKeyType `json:"type"`
|
||||
IsColumn bool `json:"isColumn"`
|
||||
}
|
||||
|
||||
func (a AttributeKey) Validate() error {
|
||||
switch a.DataType {
|
||||
case AttributeKeyDataTypeBool, AttributeKeyDataTypeNumber, AttributeKeyDataTypeString:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("invalid attribute dataType: %s", a.DataType)
|
||||
}
|
||||
|
||||
switch a.Type {
|
||||
case AttributeKeyTypeResource, AttributeKeyTypeTag:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("invalid attribute type: %s", a.Type)
|
||||
}
|
||||
|
||||
if a.Key == "" {
|
||||
return fmt.Errorf("key is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterAttributeValueResponse struct {
|
||||
@ -345,9 +367,9 @@ type BuilderQuery struct {
|
||||
QueryName string `json:"queryName"`
|
||||
DataSource DataSource `json:"dataSource"`
|
||||
AggregateOperator AggregateOperator `json:"aggregateOperator"`
|
||||
AggregateAttribute string `json:"aggregateAttribute,omitempty"`
|
||||
AggregateAttribute AttributeKey `json:"aggregateAttribute,omitempty"`
|
||||
Filters *FilterSet `json:"filters,omitempty"`
|
||||
GroupBy []string `json:"groupBy,omitempty"`
|
||||
GroupBy []AttributeKey `json:"groupBy,omitempty"`
|
||||
Expression string `json:"expression"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Having []Having `json:"having,omitempty"`
|
||||
@ -356,7 +378,7 @@ type BuilderQuery struct {
|
||||
PageSize uint64 `json:"pageSize"`
|
||||
OrderBy []OrderBy `json:"orderBy,omitempty"`
|
||||
ReduceTo ReduceToOperator `json:"reduceTo,omitempty"`
|
||||
SelectColumns []string `json:"selectColumns,omitempty"`
|
||||
SelectColumns []AttributeKey `json:"selectColumns,omitempty"`
|
||||
}
|
||||
|
||||
func (b *BuilderQuery) Validate() error {
|
||||
@ -376,7 +398,7 @@ func (b *BuilderQuery) Validate() error {
|
||||
if err := b.AggregateOperator.Validate(); err != nil {
|
||||
return fmt.Errorf("aggregate operator is invalid: %w", err)
|
||||
}
|
||||
if b.AggregateAttribute == "" && b.AggregateOperator.RequireAttribute() {
|
||||
if b.AggregateAttribute == (AttributeKey{}) && b.AggregateOperator.RequireAttribute() {
|
||||
return fmt.Errorf("aggregate attribute is required")
|
||||
}
|
||||
}
|
||||
@ -388,11 +410,20 @@ func (b *BuilderQuery) Validate() error {
|
||||
}
|
||||
if b.GroupBy != nil {
|
||||
for _, groupBy := range b.GroupBy {
|
||||
if groupBy == "" {
|
||||
return fmt.Errorf("group by cannot be empty")
|
||||
if groupBy.Validate() != nil {
|
||||
return fmt.Errorf("group by is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.SelectColumns != nil {
|
||||
for _, selectColumn := range b.SelectColumns {
|
||||
if selectColumn.Validate() != nil {
|
||||
return fmt.Errorf("select column is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.Expression == "" {
|
||||
return fmt.Errorf("expression is required")
|
||||
}
|
||||
@ -411,13 +442,18 @@ func (f *FilterSet) Validate() error {
|
||||
if f.Operator != "" && f.Operator != "AND" && f.Operator != "OR" {
|
||||
return fmt.Errorf("operator must be AND or OR")
|
||||
}
|
||||
for _, item := range f.Items {
|
||||
if err := item.Key.Validate(); err != nil {
|
||||
return fmt.Errorf("filter item key is invalid: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FilterItem struct {
|
||||
Key string `json:"key"`
|
||||
Value interface{} `json:"value"`
|
||||
Operator string `json:"op"`
|
||||
Key AttributeKey `json:"key"`
|
||||
Value interface{} `json:"value"`
|
||||
Operator string `json:"op"`
|
||||
}
|
||||
|
||||
type OrderBy struct {
|
||||
|
Loading…
x
Reference in New Issue
Block a user