最近项目要升级原有的kibana,于是学习了一下kibana的源码。kibana的功能是真的又多又强,代码量也是非常多。
前言
kibana7.6的实现包含node层以及web层。node层用的hapi框架,web层涉及多种框架react、angular以及rxjs的混用,编码语言typescript与javascript并存。打开package.json收获n多插件。
代码结构
kibana web相关实现基本归纳在public文件夹下,node端实现在server下
- config 配置文件 kibana.yml 配置kibana的访问方式以及与es相关的配置 大多数问题都可以在配置中找到解决方案
#server.port: 5601
#server.host: "localhost"
# basePath可自定义访问前缀,当rewriteBasePath为true时会被去掉
#server.basePath: ""
# 该项定义决定kibana请求是否带basePath的前缀,以及在做反向代理时是否要重写地址
# 在6.3版本之前默认值是false,7.0之后默认为true
#server.rewriteBasePath: false
#server.maxPayloadBytes: 1048576
#server.name: "your-hostname"
# kibana连接es的地址
#elasticsearch.hosts: ["http://localhost:9200"]
# es集群索引
# dashboards. Kibana creates a new index if the index doesn't already exist.
#kibana.index: ".kibana"
#kibana.defaultAppId: "home"
# 用户名密码用于权限校验
#elasticsearch.username: "kibana_system"
#elasticsearch.password: "pass"
# Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively.
# These settings enable SSL for outgoing requests from the Kibana server to the browser.
#server.ssl.enabled: false
#server.ssl.certificate: /path/to/your/server.crt
#server.ssl.key: /path/to/your/server.key
# Optional settings that provide the paths to the PEM-format SSL certificate and key files.
# These files are used to verify the identity of Kibana to Elasticsearch and are required when
# xpack.security.http.ssl.client_authentication in Elasticsearch is set to required.
#elasticsearch.ssl.certificate: /path/to/your/client.crt
#elasticsearch.ssl.key: /path/to/your/client.key
# Optional setting that enables you to specify a path to the PEM file for the certificate
# authority for your Elasticsearch instance.
#elasticsearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ]
# To disregard the validity of SSL certificates, change this setting's value to 'none'.
#elasticsearch.ssl.verificationMode: full
# Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of
# the elasticsearch.requestTimeout setting.
#elasticsearch.pingTimeout: 1500
# Time in milliseconds to wait for responses from the back end or Elasticsearch. This value
# must be a positive integer.
#elasticsearch.requestTimeout: 30000
# 可以传递到es的请求头白名单
#elasticsearch.requestHeadersWhitelist: [ authorization ]
# 可以自定义传递到es的请求头
#elasticsearch.customHeaders: {}
# Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable.
#elasticsearch.shardTimeout: 30000
# Logs queries sent to Elasticsearch. Requires logging.verbose set to true.
#elasticsearch.logQueries: false
# Specifies the path where Kibana creates the process ID file.
#pid.file: /var/run/kibana.pid
# Enables you to specify a file where Kibana stores log output.
#logging.dest: stdout
# Set the value of this setting to true to suppress all logging output.
#logging.silent: false
# Set the value of this setting to true to suppress all logging output other than error messages.
#logging.quiet: false
# Set the value of this setting to true to log all events, including system usage information
# and all requests.
#logging.verbose: false
# Set the interval in milliseconds to sample system and process performance
# metrics. Minimum is 100ms. Defaults to 5000.
#ops.interval: 5000
# 国际化 中文对应值为zh-CN.
#i18n.locale: "en"
- packages 提供了一些es及kbn的工具包
- scripts 一些启动脚本
- x-pack kibana提供的插件用于权限控制,提供登录、space分离等
- src 核心代码实现
kibana运行流程
- 入口src/cli/index.js 开始node环境的安装校验以及apm监控的注册以及命令参数的初始化
- 调用src/core/server/bootstrap.ts中bootstrap方法启动服务。其中的 root.setup()以及root.start()则是注册及启动服务的核心实现。
/**
*
* @internal
* @param param0 - options
*/
export async function bootstrap({
configs,
cliArgs,
applyConfigOverrides,
features,
}: BootstrapArgs) {
if (cliArgs.repl && !features.isReplModeSupported) {
onRootShutdown('Kibana REPL mode can only be run in development mode.');
}
const env = Env.createDefault({
configs,
cliArgs,
isDevClusterMaster: isMaster && cliArgs.dev && features.isClusterModeSupported,
});
const rawConfigService = new RawConfigService(env.configs, applyConfigOverrides);
rawConfigService.loadConfig();
const root = new Root(rawConfigService, env, onRootShutdown);
process.on('SIGHUP', () => reloadLoggingConfig());
// This is only used by the LogRotator service
// in order to be able to reload the log configuration
// under the cluster mode
process.on('message', msg => {
if (!msg || msg.reloadLoggingConfig !== true) {
return;
}
reloadLoggingConfig();
});
function reloadLoggingConfig() {
const cliLogger = root.logger.get('cli');
cliLogger.info('Reloading logging configuration due to SIGHUP.', { tags: ['config'] });
try {
rawConfigService.reloadConfig();
} catch (err) {
return shutdown(err);
}
cliLogger.info('Reloaded logging configuration due to SIGHUP.', { tags: ['config'] });
}
process.on('SIGINT', () => shutdown());
process.on('SIGTERM', () => shutdown());
function shutdown(reason?: Error) {
rawConfigService.stop();
return root.shutdown(reason);
}
try {
await root.setup();
await root.start();
} catch (err) {
await shutdown(err);
}
if (cliArgs.optimize) {
const cliLogger = root.logger.get('cli');
cliLogger.info('Optimization done.');
await shutdown();
}
}
- src/core/server/server.ts 列出了kibana服务所依赖的对象实例
- 其中ElasticsearchService初始化得到esClusterClient实例,所有kibana到es的请求都是通过该client
- LegacyService创建kbnServer,该文件引入了src/legacy/server/kibana_service.ts
- RenderingService setup中实现了渲染方法,render模板在 src/core/server/rendering/views/template
public readonly configService: ConfigService;
private readonly capabilities: CapabilitiesService;
private readonly context: ContextService;
private readonly elasticsearch: ElasticsearchService;
private readonly http: HttpService;
private readonly rendering: RenderingService;
private readonly legacy: LegacyService;
private readonly log: Logger;
private readonly plugins: PluginsService;
private readonly savedObjects: SavedObjectsService;
private readonly uiSettings: UiSettingsService;
private readonly uuid: UuidService;
constructor(
rawConfigProvider: RawConfigurationProvider,
public readonly env: Env,
private readonly logger: LoggerFactory
) {
this.log = this.logger.get('server');
this.configService = new ConfigService(rawConfigProvider, env, logger);
const core = { coreId, configService: this.configService, env, logger };
this.context = new ContextService(core);
this.http = new HttpService(core);
this.rendering = new RenderingService(core);
this.plugins = new PluginsService(core);
this.legacy = new LegacyService(core);
this.elasticsearch = new ElasticsearchService(core);
this.savedObjects = new SavedObjectsService(core);
this.uiSettings = new UiSettingsService(core);
this.capabilities = new CapabilitiesService(core);
this.uuid = new UuidService(core);
}
- src/legacy/server/kibana_service.ts -> src/legacy/ui/ui_mixin.js 整合了ui层的一些实现
- uiRenderMixin 中实现了renderApp服务的入口
export async function uiMixin(kbnServer) {
await kbnServer.mixin(uiAppsMixin);
await kbnServer.mixin(uiBundlesMixin);
await kbnServer.mixin(uiSettingsMixin);
await kbnServer.mixin(fieldFormatsMixin);
await kbnServer.mixin(tutorialsMixin);
await kbnServer.mixin(uiRenderMixin);
}
kibana页面资源加载
找到模板文件 template.js.hbs(src/legacy/ui/ui/ui_render/bootstrap)可以看到页面加载的资源
window.onload = function () {
var files = [
'{{dllBundlePath}}/vendors_runtime.bundle.dll.js',
{{#each dllJsChunks}}
'{{this}}',
{{/each}}
'{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedDepsFilename}}',
'{{regularBundlePath}}/commons.bundle.js',
'{{regularBundlePath}}/{{appId}}.bundle.js'
];
var failure = function () {
// make subsequent calls to failure() noop
failure = function () {};
var err = document.createElement('h1');
err.style['color'] = 'white';
err.style['font-family'] = 'monospace';
err.style['text-align'] = 'center';
err.style['background'] = '#F44336';
err.style['padding'] = '25px';
err.innerText = document.querySelector('[data-error-message]').dataset.errorMessage;
document.body.innerHTML = err.outerHTML;
}
function loadStyleSheet(path) {
var dom = document.createElement('link');
dom.addEventListener('error', failure);
dom.setAttribute('rel', 'stylesheet');
dom.setAttribute('type', 'text/css');
dom.setAttribute('href', path);
document.head.appendChild(dom);
}
function createJavascriptElement(path) {
var dom = document.createElement('script');
dom.setAttribute('defer', 'defer');
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}
{{#each styleSheetPaths}}
loadStyleSheet('{{this}}');
{{/each}}
(function next() {
var file = files.shift();
if (!file) return;
var dom = document.createElement('script');
dom.setAttribute('async', '');
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}());
};
}