Compare commits

..

1 Commits

Author SHA1 Message Date
Pablo Fernandez
f613a6f1a4 Add missing frontmatter. Remove duplicate URLs 2026-08-06 17:48:57 +01:00
6 changed files with 220 additions and 137 deletions

View File

@@ -1,3 +1,11 @@
---
build:
list: never
publishResources: false
render: never
sitemap:
disable: true
---
VictoriaMetrics Observability Stack integrates with AI assistants through [MCP servers](https://docs.victoriametrics.com/ai-tools/#mcp-servers)
and [agent skills](https://docs.victoriametrics.com/ai-tools/#agent-skills).
The integrations allow AI agents and automation tools to query Metrics, Logs, and Traces, analyze telemetry data,

View File

@@ -1,3 +1,11 @@
---
build:
list: never
publishResources: false
render: never
sitemap:
disable: true
---
Several VictoriaMetrics components can connect to cloud storage to read or write object data.
The following table shows the supported types of storage for each component:

View File

@@ -1,3 +1,11 @@
---
build:
list: never
publishResources: false
render: never
sitemap:
disable: true
---
Using [Grafana](https://grafana.com/) with [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/) is an effective way to provide [multi-tenant](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#multitenancy) access to your metrics, logs, and traces.
vmauth provides a way to authenticate users using [JWT tokens](https://en.wikipedia.org/wiki/JSON_Web_Token) {{% available_from "v1.138.0" %}} issued by an external identity provider.
Those tokens can include information about the user and their tenant, which vmauth can use to restrict access so users only see metrics in their own tenant.

View File

@@ -1,3 +1,11 @@
---
build:
list: never
publishResources: false
render: never
sitemap:
disable: true
---
VictoriaMetrics software provides native [OpenTelemetry](https://opentelemetry.io/) ingestion across **metrics**, **logs**, and **traces** via dedicated components.
This allows running OpenTelemetry-based observability pipeline with VictoriaMetrics software as your backend.
@@ -88,4 +96,4 @@ Depending on the Grafana datasource plugin there could be multiple correlations
1. Trace to metrics, metric to logs, metric to traces - see [correlations via VictoriaMetrics plugin](https://docs.victoriametrics.com/victoriametrics/integrations/grafana/datasource/#correlations).
1. Metrics to logs or traces correlations are possible via Prometheus datasource as well.
1. Plugins Tempo, Jaeger, and Zipkin can correlate with logs or metrics using [Trace to logs](https://grafana.com/docs/grafana/latest/explore/trace-integration/#trace-to-logs)
and [Trace to metrics](https://grafana.com/docs/grafana/latest/visualizations/explore/trace-integration/#trace-to-metrics) feature.
and [Trace to metrics](https://grafana.com/docs/grafana/latest/visualizations/explore/trace-integration/#trace-to-metrics) feature.

View File

@@ -1,3 +1,11 @@
---
build:
list: never
publishResources: false
render: never
sitemap:
disable: true
---
VictoriaMetrics offers public playgrounds where you can try the full observability stack online.
Some playgrounds are based on the [OpenTelemetry Astronomy Shop demo](https://github.com/open-telemetry/opentelemetry-demo), a sample microservices application that generates realistic metrics, logs, and traces. Other playgrounds use benchmark workloads such as [prometheus-benchmark](https://github.com/VictoriaMetrics/prometheus-benchmark) to demonstrate ingestion and query performance for Prometheus-compatible systems.
@@ -158,4 +166,4 @@ Iximiuz Labs provides various [learning-by-doing resources for VictoriaMetrics](
- [VictoriaMetrics cluster](https://labs.iximiuz.com/playgrounds/victoriametrics-cluster)
- [VictoriaMetrics on Kubernetes](https://labs.iximiuz.com/playgrounds/victoriametrics-kubernetes)
Iximiuz Labs requires a [free account](https://labs.iximiuz.com/signup) to access the materials.
Iximiuz Labs requires a [free account](https://labs.iximiuz.com/signup) to access the materials.

View File

@@ -142,67 +142,40 @@ func subInt64NoOverflow(a, b int64) int64 {
// - Fractional. For example, 1234567890.123
// - Scientific. For example, 1.23456789e9
func TryParseUnixTimestamp(s string) (int64, bool) {
s, exp, ok := parseExponent(s)
if expIdx := getExpIndex(s); expIdx >= 0 {
// The timestamp is a scientific number such as 1.234e5
decimalExp, ok := tryParseInt64(s[expIdx+1:])
if !ok {
return 0, false
}
n, ok := tryParseScientificUnixTimestamp(s[:expIdx], decimalExp)
if !ok {
return 0, false
}
return n, true
}
dotIdx := strings.IndexByte(s, '.')
if dotIdx < 0 {
// The timestamp is integer.
n, ok := tryParseInt64(s)
if !ok {
return 0, false
}
return getUnixTimestampNanoseconds(n), true
}
// The timestamp is fractional.
intStr := s[:dotIdx]
fracStr := s[dotIdx+1:]
n, ok := tryParseFractionalUnixTimestamp(intStr, fracStr)
if !ok {
return 0, false
}
whole, frac, fracExp, ok := parseFraction(s)
if !ok {
return 0, false
}
// Move decimal point `exp` positions to the right.
if whole, ok = scale10xNoOverflow(whole, exp); !ok {
return 0, false
}
if exp >= fracExp {
if frac, ok = scale10xNoOverflow(frac, exp-fracExp); !ok {
return 0, false
}
fracExp = 0
} else {
if whole, ok = addNoOverflow(whole, firstDigits(frac, fracExp-exp)); !ok {
return 0, false
}
frac = lastDigits(frac, fracExp-exp)
fracExp -= exp
}
// Move decimal point `tsExp` positions to the right.
tsExp := getUnixTimestampExponent(whole)
if whole, ok = scale10xNoOverflow(whole, tsExp); !ok {
return 0, false
}
if tsExp >= fracExp {
if frac, ok = scale10xNoOverflow(frac, tsExp-fracExp); !ok {
return 0, false
}
} else {
frac = firstDigits(frac, fracExp-tsExp)
}
return addNoOverflow(whole, frac)
return n, true
}
func parseExponent(s string) (string, int, bool) {
i := getExponentIndex(s)
if i == -1 {
return s, 0, true
}
exp, ok := tryParseInt64(s[i+1:])
if !ok {
return "", 0, false
}
if exp < 0 || maxExponent < exp {
return "", 0, false
}
return s[:i], int(exp), true
}
func getExponentIndex(s string) int {
func getExpIndex(s string) int {
if n := strings.IndexByte(s, 'e'); n >= 0 {
return n
}
@@ -212,111 +185,151 @@ func getExponentIndex(s string) int {
return -1
}
// TODO: check fraction contains only digits (add test)
// TODO: truncate to max 18 digits first, then remove trailing zeroes
func parseFraction(s string) (whole int64, frac int64, fracExp int, ok bool) {
if len(s) == 0 || s == "." {
return 0, 0, 0, false
func tryParseScientificUnixTimestamp(s string, decimalExp int64) (int64, bool) {
if decimalExp < 0 {
// Negative exponents on a fractional mantissa are intentionally not
// supported. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/11268
return 0, false
}
var negative bool
if strings.HasPrefix(s, "-") {
s = s[1:]
negative = true
dotIdx := strings.IndexByte(s, '.')
if dotIdx < 0 {
n, ok := tryParseInt64(s)
if !ok {
return 0, false
}
n, ok = multiplyByDecimalExp(n, decimalExp)
if !ok {
return 0, false
}
return getUnixTimestampNanoseconds(n), true
}
var wholeStr, fracStr string
i := strings.IndexByte(s, '.')
if i == -1 {
wholeStr = s
} else if i == 0 {
fracStr = s[i+1:]
} else if i == len(s)-1 {
wholeStr = s[:i]
intStr := s[:dotIdx]
fracStr := s[dotIdx+1:]
if decimalExp >= int64(len(fracStr)) {
// The exponent shifts the decimal point past every fractional digit.
n, ok := tryParseDecimalMantissaAsInt(intStr, fracStr)
if !ok {
return 0, false
}
decimalExp -= int64(len(fracStr))
n, ok = multiplyByDecimalExp(n, decimalExp)
if !ok {
return 0, false
}
return getUnixTimestampNanoseconds(n), true
}
// The exponent leaves fractional digits, e.g. 1.784144612388E9 == 1784144612.388
if decimalExp >= int64(len(decimalMultipliers)) {
return 0, false
}
decimalExpInt := int(decimalExp)
intStr = s[:dotIdx] + fracStr[:decimalExpInt]
fracStr = fracStr[decimalExpInt:]
return tryParseFractionalUnixTimestamp(intStr, fracStr)
}
func tryParseDecimalMantissaAsInt(intStr, fracStr string) (int64, bool) {
n, ok := tryParseInt64(intStr)
if !ok {
return 0, false
}
decimalExp := int64(len(fracStr))
num, ok := multiplyByDecimalExp(n, decimalExp)
if !ok {
return 0, false
}
frac, ok := tryParseInt64(fracStr)
if !ok {
return 0, false
}
if num >= 0 {
if num > math.MaxInt64-frac {
return 0, false
}
num += frac
} else {
wholeStr = s[:i]
fracStr = s[i+1:]
}
fracStr = strings.TrimRight(fracStr, "0")
fracExp = maxExponent
if len(fracStr) < fracExp {
fracExp = len(fracStr)
}
fracStr = fracStr[0:fracExp]
if len(wholeStr) > 0 {
whole, ok = tryParseInt64(wholeStr)
if !ok {
return 0, 0, 0, false
if num < math.MinInt64+frac {
return 0, false
}
num -= frac
}
if len(fracStr) > 0 {
frac, ok = tryParseInt64(fracStr)
if !ok {
return 0, 0, 0, false
return num, true
}
func tryParseFractionalUnixTimestamp(intStr, fracStr string) (int64, bool) {
n, ok := tryParseInt64(intStr)
if !ok {
return 0, false
}
isNegative := n < 0 || n == 0 && strings.HasPrefix(intStr, "-")
multiplier, maxFracDigits := getUnixTimestampMultiplier(n)
// Truncate the fractional digits to valid length according to the unit precision.
if len(fracStr) > maxFracDigits {
// 1.123456789XXX is invalid.
tail := fracStr[maxFracDigits:]
for i := 0; i < len(tail); i++ {
if tail[i] < '0' || tail[i] > '9' {
return 0, false
}
}
fracStr = fracStr[:maxFracDigits]
}
if negative {
whole = -whole
frac = -frac
if len(fracStr) == 0 {
return n * multiplier, true
}
return whole, frac, fracExp, true
}
func tryParseInt64(s string) (int64, bool) {
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
frac, ok := tryParseInt64(fracStr)
if !ok {
return 0, false
}
return n, true
}
func addNoOverflow(a, b int64) (int64, bool) {
if a > 0 && b > 0 && a > math.MaxInt64-b {
decimalExp := len(fracStr)
if decimalExp >= len(decimalMultipliers) {
return 0, false
}
if a < 0 && b < 0 && a < math.MinInt64-b {
n *= multiplier
scale := decimalMultipliers[decimalExp]
frac *= multiplier / scale
if isNegative {
if n < math.MinInt64+frac {
return 0, false
}
return n - frac, true
}
if n > math.MaxInt64-frac {
return 0, false
}
return a + b, true
return n + frac, true
}
func firstDigits(i int64, n int) int64 {
return i / decimalMultipliers[n]
}
func multiplyByDecimalExp(n int64, decimalExp int64) (int64, bool) {
if decimalExp < 0 {
return 0, false
}
if decimalExp >= int64(len(decimalMultipliers)) {
return 0, false
}
if decimalExp == 0 {
return n, true
}
func lastDigits(i int64, n int) int64 {
return i % decimalMultipliers[n]
}
m := decimalMultipliers[decimalExp]
func scale10xNoOverflow(n int64, exp int) (int64, bool) {
m := decimalMultipliers[exp]
if n >= 0 && n > math.MaxInt64/m || n < 0 && n < math.MinInt64/m {
return 0, false
}
return n * m, true
}
const maxExponent = 18
var decimalMultipliers = [...]int64{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18}
func getUnixTimestampExponent(n int64) int {
if n <= maxValidSecond && n >= minValidSecond {
// The timestamp is in seconds.
return 9
}
if n <= maxValidMilli && n >= minValidMilli {
// The timestamp is in milliseconds.
return 6
}
if n <= maxValidMicro && n >= minValidMicro {
// The timestamp is in microseconds.
return 3
}
// The timestamp is in nanoseconds
return 0
}
var decimalMultipliers = [...]int64{0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18}
const (
maxValidSecond = math.MaxInt64 / 1_000_000_000
@@ -326,3 +339,33 @@ const (
minValidMilli = math.MinInt64 / 1_000_000
minValidMicro = math.MinInt64 / 1_000
)
func getUnixTimestampNanoseconds(n int64) int64 {
multiplier, _ := getUnixTimestampMultiplier(n)
return n * multiplier
}
func getUnixTimestampMultiplier(n int64) (int64, int) {
if n <= maxValidSecond && n >= minValidSecond {
// The timestamp is in seconds.
return 1e9, 9
}
if n <= maxValidMilli && n >= minValidMilli {
// The timestamp is in milliseconds.
return 1e6, 6
}
if n <= maxValidMicro && n >= minValidMicro {
// The timestamp is in microseconds.
return 1e3, 3
}
// The timestamp is in nanoseconds
return 1, 0
}
func tryParseInt64(s string) (int64, bool) {
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0, false
}
return n, true
}