聊聊loki的Query

本文主要研究一下loki的Query

Query

loki/pkg/logql/engine.go

// Query is a LogQL query to be executed.
type Query interface {
	// Exec processes the query.
	Exec(ctx context.Context) (Result, error)
}

// Result is the result of a query execution.
type Result struct {
	Data       promql_parser.Value
	Statistics stats.Result
}

Query接口定义了Exec方法,返回Result;Result定义了Data、Statistics属性

Exec

loki/pkg/logql/engine.go

// Exec Implements `Query`. It handles instrumentation & defers to Eval.
func (q *query) Exec(ctx context.Context) (Result, error) {
	log, ctx := spanlogger.New(ctx, "query.Exec")
	defer log.Finish()

	rangeType := GetRangeType(q.params)
	timer := prometheus.NewTimer(queryTime.WithLabelValues(string(rangeType)))
	defer timer.ObserveDuration()

	// records query statistics
	var statResult stats.Result
	start := time.Now()
	ctx = stats.NewContext(ctx)

	data, err := q.Eval(ctx)

	statResult = stats.Snapshot(ctx, time.Since(start))
	statResult.Log(level.Debug(log))

	status := "200"
	if err != nil {
		status = "500"
		if errors.Is(err, ErrParse) || errors.Is(err, ErrPipeline) || errors.Is(err, ErrLimit) {
			status = "400"
		}
	}

	if q.record {
		RecordMetrics(ctx, q.params, status, statResult)
	}

	return Result{
		Data:       data,
		Statistics: statResult,
	}, err
}

Exec方法执行q.Eval(ctx)及stats.Snapshot

Eval

loki/pkg/logql/engine.go

func (q *query) Eval(ctx context.Context) (promql_parser.Value, error) {
	ctx, cancel := context.WithTimeout(ctx, q.timeout)
	defer cancel()

	expr, err := q.parse(ctx, q.params.Query())
	if err != nil {
		return nil, err
	}

	switch e := expr.(type) {
	case SampleExpr:
		value, err := q.evalSample(ctx, e)
		return value, err

	case LogSelectorExpr:
		iter, err := q.evaluator.Iterator(ctx, e, q.params)
		if err != nil {
			return nil, err
		}

		defer helpers.LogErrorWithContext(ctx, "closing iterator", iter.Close)
		streams, err := readStreams(iter, q.params.Limit(), q.params.Direction(), q.params.Interval())
		return streams, err
	default:
		return nil, errors.New("Unexpected type (%T): cannot evaluate")
	}
}

Eval方法执行q.parse解析为Expr,之后根据Expr的类型做不同处理,如果是SampleExpr类型执行q.evalSample;如果是LogSelectorExpr类型则执行q.evaluator.Iterator

Snapshot

loki/pkg/logql/stats/context.go

func Snapshot(ctx context.Context, execTime time.Duration) Result {
	// ingester data is decoded from grpc trailers.
	res := decodeTrailers(ctx)
	// collect data from store.
	s, ok := ctx.Value(storeKey).(*StoreData)
	if ok {
		res.Store.TotalChunksRef = s.TotalChunksRef
		res.Store.TotalChunksDownloaded = s.TotalChunksDownloaded
		res.Store.ChunksDownloadTime = s.ChunksDownloadTime.Seconds()
	}
	// collect data from chunks iteration.
	c, ok := ctx.Value(chunksKey).(*ChunkData)
	if ok {
		res.Store.HeadChunkBytes = c.HeadChunkBytes
		res.Store.HeadChunkLines = c.HeadChunkLines
		res.Store.DecompressedBytes = c.DecompressedBytes
		res.Store.DecompressedLines = c.DecompressedLines
		res.Store.CompressedBytes = c.CompressedBytes
		res.Store.TotalDuplicates = c.TotalDuplicates
	}

	existing, err := GetResult(ctx)
	if err != nil {
		res.ComputeSummary(execTime)
		return res
	}

	existing.Merge(res)
	existing.ComputeSummary(execTime)
	return *existing

}

Snapshot方法从ctx.Value取出StoreData及ChunkData计算res,然后再取出Result,进行Merge及ComputeSummary

小结

loki的Query接口定义了Exec方法,返回Result;Result定义了Data、Statistics属性;query实现了Query接口,其Exec方法执行q.Eval(ctx)及stats.Snapshot。

doc

06-04
### Loki IT相关技术概述 Loki 是一个由 Grafana Labs 开发的日志聚合系统,灵感来源于 Prometheus[^2]。它通过使用标签(labels)来组织和查询日志数据,从而实现了高效的日志管理和分析能力。以下是关于 Loki 的一些关键技术点: #### 1. 标签的使用 Loki 使用标签来标识日志流,类似于 Prometheus 中的指标标签。这些标签可以轻松地将应用程序的指标与日志数据关联起来[^1]。例如,在配置文件中定义的 `{job="myapp"}` 标签可以帮助用户快速筛选出特定服务的日志。 #### 2. 架构特点 Loki 的架构设计使其能够高效处理大规模日志数据。其主要特点包括: - **分布式架构**:支持水平扩展,适用于高吞吐量的日志场景。 - **无索引存储**:通过标签组织数据,避免了传统日志系统的复杂索引结构。 - **高效存储**:采用压缩和切片技术降低存储成本。 - **实时查询**:支持低延迟的日志检索和过滤操作。 - **简化部署**:与 Prometheus 和 Grafana 的无缝集成降低了部署复杂度[^2]。 #### 3. 查询语言 Loki 使用一种类似于 PromQL 的查询语言进行日志查询。用户可以通过以下方式构建查询: - **过滤器**:`|=`, `!=`, `|~`, `!~` 等符号用于筛选日志内容[^3]。 - **正则表达式**:支持 RE2 语法,并可设置区分大小写选项。 - **时间范围**:通过 `query_range` 配置项实现对历史日志的分段查询[^4]。 #### 4. 数据存储与优化 Loki 将日志以文本格式存储,并通过 Promtail 采集工具上传至 Loki 服务器。为了优化性能,建议采取以下措施: - **数据压缩**:减少存储空间占用。 - **数据切片**:将日志按时间或大小分割。 - **保留策略**:合理设置日志保留周期以平衡存储需求和查询效率[^3]。 #### 5. 安全性与扩展性 Loki 提供了多种安全机制以保护日志数据的安全性,同时支持与其他云原生工具的整合。例如,通过 Redis 缓存查询结果可以显著提升性能[^4]。 ```python # 示例:使用 Python 调用 Loki API 进行日志查询 import requests def query_loki(log_query, start_time, end_time): url = "http://localhost:3100/loki/api/v1/query_range" params = { "query": log_query, "start": start_time, "end": end_time } response = requests.get(url, params=params) return response.json() log_query = '{job="myapp"} |= "error"' start_time = "2023-01-01T00:00:00Z" end_time = "2023-01-02T00:00:00Z" result = query_loki(log_query, start_time, end_time) print(result) ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值