Heka 的配置文件加载逻辑
Heka 使用的是 TOML 格式的配置文件, 有关 golang 加载 TOML 配置文件的技术请参看: http://www.cnblogs.com/ghj1976/p/4082323.html
Heka用的 读取 TOML 文件的Go库是: https://github.com/bbangert/toml 虽然跟上面文章的不是一个,但是基本差不多。
我们在hekad主进程的配置文件加载的逻辑如下:
- 在启动时,先读取hekad部分的配置内容,用于hekad的启动。
- 然后再去读其他配置节,把heka的管道启动起来。
github.com\mozilla-services\heka\cmd\hekad\main.go 文件中,我们摘要核心代码如下,就可以看到这个逻辑:
这里删除了一些跟这个逻辑过程无关的代码,方便阅读。

加载 hekad 部分配置
代码实现如下, 注意,这里处理了配置文件内置支持环境变量的功能。

对环境变量支持的文档如下:
Using Environment Variables
If you wish to use environmental variables in your config files as a way to configure values, you can simply use %ENV[VARIABLE_NAME] and the text will be replaced with the value of the environmental variable VARIABLE_NAME.
Example:
[AMQPInput]
url = "amqp://%ENV[USER]:%ENV[PASSWORD]@rabbitmq/"
exchange = "testout"
exchangeType = "fanout"
加载插件相关的配置逻辑
加载插件配置的逻辑主要是下面代码:

每个Heka的TOML配置节,对应的实体类如下:
// The TOML spec for plugin configuration options that will be pulled out by
// Heka itself before the config is passed to the Plugin.Init method. Not all
// options apply to all plugin types.
type PluginGlobals struct {
Typ string `toml:"type"`
Ticker uint `toml:"ticker_interval"`
Matcher string `toml:"message_matcher"` // Filter and Output only.
Signer string `toml:"message_signer"` // Filter and Output only.
Retries RetryOptions
Encoder string // Output only.
UseFraming *bool `toml:"use_framing"` // Output only.
CanExit *bool `toml:"can_exit"`
}
对应的解析时的逻辑如下:
func (self *PipelineConfig) loadPluginGlobals(section *ConfigSection) (err error) {
// Set up default retry policy.
pGlobals := new(PluginGlobals)
pGlobals.Retries = RetryOptions{
MaxDelay: "30s",
Delay: "250ms",
MaxRetries: -1,
}
if err = toml.PrimitiveDecode(section.tomlSection, pGlobals); err != nil {
err = fmt.Errorf("Unable to decode config for plugin '%s': %s",
section.name, err)
return
}
if pGlobals.Typ == "" {
pGlobals.Typ = section.name
}
if _, ok := AvailablePlugins[pGlobals.Typ]; !ok {
err = fmt.Errorf("No registered plugin type: %s", pGlobals.Typ)
} else {
section.globals = pGlobals
}
return
}
补充说明如下:
- 如果 type为空, 则 这个节的名字就是 type
- 插件要起作用,需要 RegisterPlugin , 参看 http://hekad.readthedocs.org/en/v0.8.0/developing/plugin.html
插件的类型有5种,都是在名字或者type上可以看出来的, 对应的判断类型的代码如下:
var PluginTypeRegex = regexp.MustCompile("(Decoder|Encoder|Filter|Input|Output)$")
func getPluginCategory(pluginType string) string {
pluginCats := PluginTypeRegex.FindStringSubmatch(pluginType)
if len(pluginCats) < 2 {
return ""
}
return pluginCats[1]
}
加载配置文件这里,代码在 LoadFromConfigFile 函数, 这里其实就主要做了上面两个事情,外加不同类型插件的特殊处理。
// Loads all plugin configuration from a TOML configuration file. The
// PipelineConfig should be already initialized via the Init function before
// this method is called.
func (self *PipelineConfig) LoadFromConfigFile(filename string) (err error) {
var configFile ConfigFile
// 更新配置文件中,自定义变量(环境变量)
contents, err := ReplaceEnvsFile(filename)
if err != nil {
return err
}
// TOML 解析成 configFile
if _, err = toml.Decode(contents, &configFile); err != nil {
return fmt.Errorf("Error decoding config file: %s", err)
}
var (
errcnt uint
protobufDRegistered bool
protobufERegistered bool
)
sectionsByCategory := make(map[string][]*ConfigSection)
// Load all the plugin globals and file them by category.
for name, conf := range configFile {
if name == HEKA_DAEMON {
continue
}
log.Printf("Pre-loading: [%s]\n", name)
section := &ConfigSection{
name: name,
tomlSection: conf,
}
// 加载插件配置文件, 这里面做了插件注册的检查
if err = self.loadPluginGlobals(section); err != nil {
self.log(err.Error())
errcnt++
continue
}
// 获取插件的类型
category := getPluginCategory(section.globals.Typ)
if category == "" {
self.log(fmt.Sprintf("Type doesn't contain valid plugin name: %s\n",
section.globals.Typ))
errcnt++
continue
}
// 特殊插件类型的处理
section.category = category
if section.globals.Typ == "MultiDecoder" {
// Special case MultiDecoders so we can make sure they get
// registered *after* all possible subdecoders.
sectionsByCategory["MultiDecoder"] = append(sectionsByCategory["MultiDecoder"],
section)
} else {
sectionsByCategory[category] = append(sectionsByCategory[category], section)
}
if name == "ProtobufDecoder" {
protobufDRegistered = true
}
if name == "ProtobufEncoder" {
protobufERegistered = true
}
}
// Make sure ProtobufDecoder is registered.
if !protobufDRegistered {
var configDefault ConfigFile
toml.Decode(protobufDecoderToml, &configDefault)
log.Println("Pre-loading: [ProtobufDecoder]")
section := &ConfigSection{
name: "ProtobufDecoder",
category: "Decoder",
tomlSection: configDefault["ProtobufDecoder"],
}
if err = self.loadPluginGlobals(section); err != nil {
// This really shouldn't happen.
self.log(err.Error())
errcnt++
} else {
sectionsByCategory["Decoder"] = append(sectionsByCategory["Decoder"],
section)
}
}
// Make sure ProtobufEncoder is registered.
if !protobufERegistered {
var configDefault ConfigFile
toml.Decode(protobufEncoderToml, &configDefault)
log.Println("Pre-loading: [ProtobufEncoder]")
section := &ConfigSection{
name: "ProtobufEncoder",
category: "Encoder",
tomlSection: configDefault["ProtobufEncoder"],
}
if err = self.loadPluginGlobals(section); err != nil {
// This really shouldn't happen.
self.log(err.Error())
errcnt++
} else {
sectionsByCategory["Encoder"] = append(sectionsByCategory["Encoder"],
section)
}
}
multiDecoders := make([]multiDecoderNode, len(sectionsByCategory["MultiDecoder"]))
multiConfigs := make(map[string]*ConfigSection)
for i, section := range sectionsByCategory["MultiDecoder"] {
multiConfigs[section.name] = section
multiDecoders[i] = newMultiDecoderNode(section.name, subsFromSection(section.tomlSection))
}
multiDecoders, err = orderDependencies(multiDecoders)
if err != nil {
return err
}
for i, d := range multiDecoders {
sectionsByCategory["MultiDecoder"][i] = multiConfigs[d.name]
}
// Append MultiDecoders to the end of the Decoders list.
sectionsByCategory["Decoder"] = append(sectionsByCategory["Decoder"],
sectionsByCategory["MultiDecoder"]...)
// Force decoders and encoders to be registered before the other plugin
// types are initialized so we know they'll be there for inputs and
// outputs to use during initialization.
order := []string{"Decoder", "Encoder", "Input", "Filter", "Output"}
for _, category := range order {
for _, section := range sectionsByCategory[category] {
log.Printf("Loading: [%s]\n", section.name)
if err = self.loadSection(section); err != nil {
self.log(err.Error())
errcnt++
}
}
}
if errcnt != 0 {
return fmt.Errorf("%d errors loading plugins", errcnt)
}
return
}
Heka 的配置文件加载逻辑的更多相关文章
- Spring Boot 2.4.0正式发布,全新的配置文件加载机制(不向下兼容)
千里之行,始于足下.关注公众号[BAT的乌托邦],有Spring技术栈.MyBatis.JVM.中间件等小而美的原创专栏供以免费学习.分享.成长,拒绝浅尝辄止.本文已被 https://www.you ...
- Spring Boot源码分析-配置文件加载原理
在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...
- Spring使用环境变量控制配置文件加载
项目中需要用到很多配置文件,不同环境的配置文件是不一样的,因此如果只用一个配置文件,势必会造成配置文件混乱,这里提供一种利用环境变量控制配置文件加载的方法,如下: 一.配置环境变量 如果是window ...
- struts几个配置文件加载顺序_2015.01.04
struts几个配置文件加载顺序: 01:struts-default.xml 02:struts-plugin.xml 03:struts.xml 04:struts.properties 05:w ...
- asp.netcore 深入了解配置文件加载过程
前言 配置文件中程序运行中,担当着不可或缺的角色:通常情况下,使用 visual studio 进行创建项目过程中,项目配置文件会自动生成在项目根目录下,如 appsettings.json, ...
- bash 的配置文件加载顺序
bash配置文件的加载顺序和登陆方式有关,下面先介绍下登陆方式. 1 登陆方式有2种 登陆式SHELL: su - oracle su -l oracle 正常从终端登陆 非登录式SHELL: ...
- 解决eclipse部署maven时,src/main/resources里面配置文件加载不到webapp下classes路径下的问题
解决eclipse部署maven时,src/main/resources里面配置文件加载不到webapp下classes路径下的问题. 有时候是src/main/resources下面的,有时候是sr ...
- node 加载逻辑
[node 加载逻辑] require(X) from module at path Y . If X is a core module, a. return the core module b. S ...
- springboot的yaml基础语法与取值,配置类,配置文件加载优先级
1.基本语法k:(空格)v:表示一对键值对(一个空格必须有):以空格的缩进来控制层级关系:只要是左对齐的一列数据,都是同一个层级的属性和值也是大小写敏感: server: port: 8081 pat ...
随机推荐
- [ActionScript 3.0] 运用Color类interpolateColor静态方法绘制渐变色
以下类可直接作为文档类测试,效果如图: package { import fl.motion.Color; import flash.display.GradientType; import flas ...
- 7,CountDownLatch 与 CyclicBarrier 的 区别
CountDownLatch : 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CountDownLatch.由于调用了 countDo ...
- unity里的c#
类方法:start只进行一次,update每一帧都会进行调用 拖动脚步到对应物体就可以将其关联 定义变量与c++相似 F2修改文件名 脚本的类名要与文件名保持一致 数组的定义:1.类型[] 数组名={ ...
- HTML5本地存储——IndexedDB二:索引
HTML5本地存储——IndexedDB(二:索引) 在HTML5本地存储——IndexedDB(一:基本使用)中介绍了关于IndexedDB的基本使用方法,很不过瘾,这篇我们来看看indexed ...
- sqlalchemy中使用event设置条件触发短信与邮件通知
一.原因 近期在做短信与邮件通知系统.使用到了这一块.例如,当订单完成以后进行邮件短信的通知.虽然可以采用直接调用接口的方式实现,但有几个原因让我希望使用条件触发的方式 1.由于系统中支持线上线下以及 ...
- js控制输入框只能输入数字不能输入其他字符
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 启动与销毁Activity
启动与销毁Activity 编写:kesenhoo - 原文:http://developer.android.com/training/basics/activity-lifecycle/start ...
- vim编辑器基本操作及文件权限,sudo命令等介绍
一:vim 操作命令,在命令模式下操作 pageup 往上翻页 pagedown 往下翻页 H 移动到屏幕首行 gg 移动光标到文档的首行 前面加数字n表示移动到n行内容 G 移动到文档最后一行/查找 ...
- 进阶篇:5.3.1)均方根法(Root-Sum-Squares,RSS)
本章目的:了解均方根法,运用均方根法. 1.定义 均方根法(Root-Sum-Squares,RSS):均方根法是统计分析法的一种,是把尺寸链中的各个尺寸公差的平方之和再开根即得到关键尺寸的公差. 其 ...
- Drupal Coder 模块远程命令执行分析(SA-CONTRIB-2016-039)
转载请注明文章出处:http://www.cnblogs.com/magic-zero/p/5787181.html 起初看到这个漏洞的时候是在exploit-db上边.地址在这里:https://w ...