diff --git a/theme/assets/png/gala-arch.png b/theme/assets/png/gala-arch.png new file mode 100644 index 0000000000000000000000000000000000000000..7f71718008dce4bd2c358a2af19a0b472ff04e40 Binary files /dev/null and b/theme/assets/png/gala-arch.png differ diff --git a/theme/assets/png/partner.png b/theme/assets/png/partner.png new file mode 100644 index 0000000000000000000000000000000000000000..28a1ec5c7254309917807e88eaf6a6a7980390e9 Binary files /dev/null and b/theme/assets/png/partner.png differ diff --git a/theme/content/en/docs/gala-docs/gopher_tech.md b/theme/content/en/docs/gala-docs/gopher_tech.md index f1f2eea33ecbc548f4d78b2d10e624a62967c307..e177c176fb11d06908ab88e12c083f1aca591981 100644 --- a/theme/content/en/docs/gala-docs/gopher_tech.md +++ b/theme/content/en/docs/gala-docs/gopher_tech.md @@ -514,33 +514,33 @@ toc: true | version | jvm_info | label | | | JVM 版本 | | info | jvm_info | gauge | | | 固定值1 | | process_start_time_seconds | jvm_process | gauge | s | | 进程起始时间 | -| process_cpu_seconds_total | jvm_process | gauge | s | | 进程已使用的CPU时间 | +| process_cpu_seconds_total | jvm_process | counter | s | | 进程已使用的CPU时间 | | classes_currently_loaded | jvm_class | gauge | | | JVM当前已加载类的数量 | -| classes_loaded_total | jvm_class | gauge | | | JVM自执行以来加载的类的总数量 | +| classes_loaded_total | jvm_class | counter | | | JVM自执行以来加载的类的总数量 | | threads_current | jvm_thread | gauge | | | JVM当前线程数 | | threads_daemon | jvm_thread | gauge | | | JVM的守护线程数 | | threads_peak | jvm_thread | gauge | | | JVM的峰值线程数 | -| threads_started_total | jvm_thread | gauge | | | JVM的已启动线程数 | +| threads_started_total | jvm_thread | counter | | | JVM的已启动线程数 | | threads_deadlocked | jvm_thread | gauge | | | JVM的死锁的线程数 | -| area | jvm_mem | key | | | JVM内存类型:heap/noheap | +| area | jvm_mem | label | | | JVM内存类型:heap/noheap | | memory_bytes_used | jvm_mem | gauge | bytes | | 给定JVM内存区域的已使用字节数 | | memory_bytes_committed | jvm_mem | gauge | bytes | | 给定JVM内存区域的已提交字节数 | | memory_bytes_max | jvm_mem | gauge | bytes | | 给定JVM内存区域的最大字节数 | | memory_bytes_init | jvm_mem | gauge | bytes | | 给定JVM内存区域的初始字节数 | -| pool | jvm_mem_pool | key | | | 内存池类型 | +| pool | jvm_mem_pool | label | | | 内存池类型 | | memory_pool_bytes_used | jvm_mem_pool | gauge | bytes | | 给定JVM内存池的已使用字节数 | | memory_pool_bytes_committed | jvm_mem_pool | gauge | bytes | | 给定JVM内存池的已提交字节数 | | memory_pool_bytes_max | jvm_mem_pool | gauge | bytes | | 给定JVM内存池的最大字节数 | | memory_pool_collection_used_bytes | jvm_mem_pool | gauge | bytes | | 给定JVM内存池最后一次垃圾回收使用的字节数 | | memory_pool_collection_committed_bytes | jvm_mem_pool | gauge | bytes | | 上一次GC内存池的大小 | | memory_pool_collection_max_bytes | jvm_mem_pool | gauge | bytes | | 上一次GC内存池的最大字节数 | -| pool | jvm_buf_pool | key | | | 缓冲池类型 | +| pool | jvm_buf_pool | label | | | 缓冲池类型 | | buffer_pool_used_bytes | jvm_buf_pool | gauge | bytes | | 给定JVM缓冲池的已用字节数 | | buffer_pool_used_buffers | jvm_buf_pool | gauge | | | 给定JVM缓冲池的已用缓冲区数 | | buffer_pool_capacity_bytes | jvm_buf_pool | gauge | bytes | | 给定JVM缓冲池的字节容量 | -| gc | jvm_gc | key | | | 垃圾回收器名字 | -| gc_collection_seconds_count | jvm_gc | gauge | | | 给定的垃圾回收器已发生的GC总次数 | -| gc_collection_seconds_sum | jvm_gc | gauge | s | | 在给定的垃圾回收器花费的总时间 | +| gc | jvm_gc | label | | | 垃圾回收器名字 | +| gc_collection_seconds_count | jvm_gc | summary | | | 给定的垃圾回收器已发生的GC总次数 | +| gc_collection_seconds_sum | jvm_gc | summary | s | | 在给定的垃圾回收器花费的总时间 | diff --git a/theme/content/en/docs/gala-docs/introducation.md b/theme/content/en/docs/gala-docs/introducation.md index fd35084d12780261bb04517b922699f7abfd3dfe..91ec41b83f7c53477b083ad9c72ad50746bc5450 100644 --- a/theme/content/en/docs/gala-docs/introducation.md +++ b/theme/content/en/docs/gala-docs/introducation.md @@ -7,28 +7,59 @@ menu: weight: 110 toc: true --- +# 介绍 + +gala-ops是一款C/S架构、基于AI的操作系统亚健康诊断工具。其基于eBPF + java agent无侵入观测技术,并以AI技术辅助,实现亚健康故障(比如性能抖动、错误率提升、系统卡顿等问题现象)分钟级诊断,简化IT基础设施的运维过程。 + # 背景 -​ 云场景中基础软件/业务应用之间的边界逐渐上移,基础软件逐渐成为云场景最重要的组成部分,而操作系统又最重要的基础软件之一。 -​ 从业界公开的数据看,云场景的一些重要故障均是与基础软件密切相关。公开数据显示现有主流云厂商月平均故障150+次数,75%的故障<1H,90%<1.5H,少量故障>5H。 +云基础设施在近几年随着云原生、无服务化等技术的实施,其运维的复杂性变得越来越有挑战性,尤其是亚健康问题特点(间歇性出现、持续时间短、问题种类多、涉及范围广等)给云基础设施故障诊断带来重要挑战。亚健康故障诊断的挑战(包括可观测能力、海量数据管理能力、AI算法的泛化能力等)在Linux场景中变的尤为突出。在openEuler开源操作系统中,现有的运维手段不足以及时发现、定位亚健康问题,存在包括:缺乏在线、持续性监控能力;缺乏应用视角精细化的观测能力;缺乏基于全栈观测数据的自动化、AI分析能力等问题。然而,针对亚健康故障的诊断能力其难点包括: -云场景的基础设施、业务场景的复杂性,导致这些故障现象大量集中基础软件(尤其是操作系统)层面,为此openEuler社区规划&孵化A-Ops项目,该项目包括基础设施监控、应用性能监控、应用安全、自动化及监控四大块功能。 +- 全栈的无侵入可观测观测能力。 +- 持续、精细化、低负载的监控能力。 +- 自适应不同应用场景的异常检测、可视化故障推导能力。 -![](./png/a-ops-arch.png) +# 项目简介 + +gala-ops的整体架构如图所示,其整体上是一个C/S架构。在生产节点gala-gopher是一个Linux后台程序,其负责提供全场景、全栈(包括Metrics、Events、Tracing等)的数据采集,其支持通过OpenTelemetry开放生态接口(支持prometheus exporter、kafka client等)将数据传递给管理节点。管理节点部署gala-spider、gala-anteater组件,分别负责集群拓扑计算、可视化根因推导; + +gala-ops架构上依赖一些开源中间件(包括prometheus、kafka、Elastic等),但亦可对接至客户IT系统现有的中间件。gala-ops架构设计提供被集成能力,可以由行业客户IT运维系统集成。其提供两类被集成方式: + +- 软件生态集成方式:可以只使用gala-gopher可观测能力(OpenTelemetry方式获取数据),亦可以使用全部能力,通过prometheus、Elastic、kafka等中间件获取观测数据、异常检测结果、可视化推导结果。 +- 工具集成方式:将gala-ops提供的能力以Grafana形式集成至客户IT运维系统内。 + +![](./png/gala-arch.png) -# 介绍 -​ 针对云场景的故障特点,根据故障发展阶段划分成:系统隐患、灰度故障、故障 三个阶段,A-Ops规划应用性能监控解决方案,该解决方案包括多个关键组件,本文用于介绍相关gala-ops系列组件。 -​ gala-ops系列组件定位:云基础设施场景中,针对基础设施**灰度故障**导致的**应用性能劣化**、卡顿系统级故障**在线诊断**。提供包括**应用性能诊断、系统性能瓶颈诊断、系统参数修复、系统实时拓扑**等特性。 +gala-ops可以给客户提供如下运维能力: -# 原理 +- 在线应用性能抖动诊断:提供数据库类应用性能在线诊断能力,包括网络类(丢包、重传、时延、TCP零窗等)问题、I/O类(磁盘慢盘、I/O性能下降等)问题,调度类(包括sysCPU冲高、死锁等)问题、内存类(OOM、泄漏等)问题等。 +- 系统性能瓶颈诊断:提供通用场景的TCP、I/O性能抖动问题诊断能力。 +- 系统隐患巡检:提供内核协议栈丢包、虚拟化网络丢包、TCP异常、I/O时延异常、系统调用异常、资源泄漏、JVM异常、应用RPC异常(包括8种常见协议的错误率、时延等)硬件故障(UCE、磁盘介质错误等)等秒级巡检能力。 +- 系统全栈I/O可观测:提供面向分布式存储场景的I/O全栈观测能力,包括GuestOS 进程级、Block层的I/O观测能力,以及虚拟化层存储前端I/O观测能力,分布式存储后端I/O观测能力。 +- 精细化性能Profiling:提供多维度(包括系统、进程、容器、Pod等多个维度)、高精度(10ms采样周期)的性能(包括CPU性能、内存占用、资源占用、系统调用等类型)火焰图、时间线图,可实时在线持续性采集。 +- K8S Pod全栈可观测及诊断:提供K8S视角的Pod集群业务流实时拓扑能力,Pod性能观测能力、DNS观测能力、SQL观测能力等。 -通过eBPF技术实现系统白盒化智能观测,实时在线完成系统架构拓扑化,在此基础完成从基础软硬件至应用现象的根因推导过程,且过程可视化。 + -三步骤如下: +gala-ops涉及的关键技术包括如下: -![](./png/Principle.png) +- 融合型非侵入观测技术:融合eBPF、Java agent等不同观测技术优点,实现多语言(支持C/C++、Java、Go等主流语言)、全软件栈(包括内核、系统调用、基础库Glibc、运行时jvm、基础中间件Nginx/Haproxy等)的观测能力。 +- 流程拓扑:基于时序化数据(L4/L7层流量等),实时计算生成时序化拓扑结构,动态展现业务集群拓扑变化。 +- 可视化根因定位:统计推理模型结合全流程拓扑,实现可视化&分钟级的问题根因诊断。 + +# 应用场景 + +​ gala-ops在openEuler等Linux环境主要面向场景包括数据库、分布式存储、虚拟化、云原生等场景。助力金融、电信、互联网等行业客户在全栈可观测的基础上实现亚健康故障分钟级诊断。 + +# 项目代码仓 + +https://gitee.com/openeuler/gala-gopher + +https://gitee.com/openeuler/gala-spider + +https://gitee.com/openeuler/gala-anteater @@ -173,44 +204,244 @@ gala-ops还依赖一些开源软件,包括kafka、arangodb、prometheus等。 ## gala-ops系统安装 -A-Ops提供了集成式部署工具A-Ops-Tools以便用户快速部署gala-ops以及其依赖的开源中间件,部署工具的使用约束说明与所有支持选项详细说明可参照[A-Ops-Tools部署工具手册](https://gitee.com/Vchanger/a-ops-tools#a-ops-tools)。 +Gala提供了集成式部署工具[Gala-Deploy-Tools](./deploy)以便用户快速部署gala-gopher、gala-ops(gala-spider/gala-inference/gala-anteater)组件、kafka/prometheus/arangodb/es/logstash/pyroscope中间件、grafana前端页面展示相关组件,并同时支持离线/在线部署两种模式。 +- kafka 用于传输 gala 软件数据 +- prometheus 用于存储 gopher metrics数据 +- arangodb 用于存储 gala-spider 生成的实时拓扑数据 +- elasticsearch/logstash 存储 gala 数据支持 grafana 前端展示 +- pyroscope 存储 gopher 火焰图数据 +- grafana 展示 gala 前端页面 -- 获取部署工具 - 1. 下载部署工具压缩包:wget https://gitee.com/Vchanger/a-ops-tools/repository/archive/master.zip --no-check-certificate - 2. 使用unzip解压压缩包后进入对应目录即可使用 +### 约束限制 -- 部署中间件:kafka/prometheus/arangodb/elasticsearch/logstash +1. 当前本工具仅支持如下OS版本:openEuler 20.03 LTS SP1(x86)、openEuler 22.03 LTS、openEuler 22.03 LTS SP1、Kylin V10(x86) +2. 在线部署模式下,本工具运行过程中会从openEuler repo源安装rpm或者从外网下载源码资源,因此内网环境在使用工具前需要提前配置好代理,便于访问外网环境,工具使用结束后建议将代理取消。 +3. 在线部署模式下,gala-gopher、gala-ops组件支持rpm包部署以及容器部署两种方式,其中gala-ops组件的rpm包部署方式仅支持openEuler 22.03 LTS SP1 版本。 +4. 离线部署模式下,gala-gopher会以rpm包的方式安装部署,gala-ops组件会以容器方式安装部署。 - 执行如下命令安装、配置、启动kafka/prometheus/arangodb/elasticsearch/logstash服务,-K/-P/-A/-E选项支持分开使用单独部署对应组件,其中-P用于配置prometheus服务端抓取消息的来源地址(即部署gala-gopher的生产节点)列表,每个地址之间用英文逗号分隔;elasticsearch/logstash由于存在依赖关系,通过-E选项统一控制、绑定安装。 +### 环境准备说明 - ``` - # sh deploy.sh middleware -K -P -E -A - ``` +准备至少两台符合OS版本与架构要求(见约束限制1)的机器(物理机、虚拟机均可)并保证机器间网络可以正常连通(在线部署模式下需要连接外网)。 + +- 机器A:**生产节点**,即需要监控运维的目标节点,上面一般运行着业务进程(如数据库、redis、Java应用),用于部署观测组件gala-gopher。 + + ***注:如果有多台生产节点,则每个节点都需要部署gala-gopher*** + + + +- 机器B:**管理节点**,用于部署kafka等中间件以及gala的异常检测、根因定位组件。这些组件的部署相对灵活,可以准备多台管理节点分开部署,只要节点之间网络通即可。 + + ***注:管理节点的机器规格建议至少为8U8G*** + + + +### 离线部署 + +gala组件的运行依赖各个中间件,因此建议按照如下顺序(中间件->gala-gopher/gala-ops->grafana)进行安装部署。 + +#### 管理节点:部署中间件 + +当前涉及的中间件包括kafka、prometheus、arangodb、elasticsearch/logstash、pyroscope共6个组件,其中elasticsearch和logstash存在依赖关系,需要绑定部署。 + +1. 离线安装包下载 + +离线部署前,需要在可连接外网的机器上下载6个中间件的安装包。本工具提供了[离线资源下载脚本](./deploy/download_offline_res.sh),[辅助脚本](./deploy/comm.sh)一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源的下载,下载内容会存放在当前目录的子目录gala_deploy_middleware下。 + +```xml +sh download_offline_res.sh middleware [os_arch] +``` -- 部署gala-ops +可选选项: - gala-ops组件支持rpm、容器镜像两种部署方式,部署时需要指定kafka、prometheus、arangodb服务器地址,当不指定时,这些中间件的地址默认使用localhost。 +- os_arch: 指定下载该架构的安装包。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64 - - rpm方式(仅支持openEuler 22.03 LTS/openEuler 22.03 LTS SP1) +注:由于kafka运行依赖java,因此下载kafka安装包时也会同时下载java-1.8.0-openjdk及其依赖包;arangodb组件需要下载容器镜像tar包,因此下载机器上需要安装docker组件。* - ``` - # sh deploy.sh ops -K -P -A - ``` +2. 工具一键部署 - - 容器镜像方式: +将gala_deploy_middleware下的所有文件和[部署脚本](./deploy/deploy.sh) ,[辅助脚本](./deploy/comm.sh) 上传到目标管理节点机器上,执行如下命令安装、配置、启动kafka/prometheus/elasticsearch/logstash/arangodb/pyroscope服务,**-K/-P/-E/-A/-p选项支持分开使用单独部署对应组件**,-S选项来指定离线安装包所在的目录。 - ``` - # sh deploy.sh ops -K -P -A --docker - ``` +```css +sh deploy.sh middleware -K <部署节点管理IP> -P -E <部署节点管理IP> -A -p -S <中间件安装包所在目录> +``` + +选项详细说明 + +| 选项 | 参数说明 | 是否必配 | +| :--------------: | :----------------------------------------------------------: | :----------------------------------: | +| -K\|--kafka | 使用该选项用于部署kafka服务器,并配置指定的监听IP地址(一般来说是当前节点的管理IP)。当不使用该选项时,不部署kafka服务 | 需要部署kafka服务时为必 | +| -P\|--prometheus | 使用该选项用于部署prometheus服务器,并配置指定的抓取消息来源(即部署gala-gopher的生产节点)地址列表,每个地址之间用英文逗号分隔,地址后可以跟随“:端口号”来指定抓取端口,当不指定时,使用默认端口8888;地址前可以加上”主机名-“来标识该地址。
例如:-P 192.168.0.1,192.168.0.2:18001,vm01-192.168.0.3:18002。当不使用该选项时,不部署prometheus服务 | 需要部署prometheus服务器时为必配 | +| -A\|--arangodb | 使用该选项用于部署并启动arangodb数据库服务,该服务默认监听全IP,因此无需指定监听IP。 | 需要部署arangodb时为必配 | +| -p\|--pyroscope | 使用该选项用于部署并启动pyroscope服务,该服务默认监听全IP,因此无需指定监听IP。 | 需要部署pyroscope服务端时必配 | +| -E\|--elastic | 使用该选项用于部署elasticsearch、logstash服务,并指定logstash读取消息的elasticsearch服务器地址(一般来说是当前节点的管理IP)。当不使用该选项时,不部署elaticsearch服务 | 需要部署elasticsearch/logstash为必配 | +| -S\|--srcdir | 离线部署时使用该选项来指定离线安装包所在的目录 | 离线部署时必配 | + +#### 生产节点:部署gala-gopher + +1. 对应版本gala-gopher及依赖包下载 + +离线部署前,需要在可连接外网的机器上下载对应版本的gala-gopher包以及其依赖的rpm包。本工具提供了[离线资源下载脚本](./deploy/download_offline_res.sh),[辅助脚本](./deploy/comm.sh)一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_gopher下。 + +```xml +sh download_offline_res.sh gopher [os_version] [os_arch] [docker] +``` +os_version、os_arch 可选项需同时配置使用: +- os_version: 指定下载该操作系统版本 gala-gopher 软件包。未配置该项时,使用当前系统版本。支持版本列表:openEuler-22.03-LTS-SP1 openEuler-22.03-LTS openEuler-20.03-LTS-SP1 kylin -- 部署grafana +- os_arch: 指定下载该架构 gala-gopher 软件包。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64 - 执行如下命令完成部署,grafana会以容器实例方式运行。 +- docker:指定下载 gala-gopher docker 镜像 tar 和 gala-gopher 配置文件(docker 运行 gala-gopher, 将配置文件映射到宿主机上)。命令示例 `sh download_offline_res.sh gopher docker `。下载 tar 包 和 gala-gopher 配置文件存放在 gala_deploy_gopher 目录下,文件名格式为`gala-gopher-[os_arch]:[os_tag].tar`。下载内容如下: ``` - # sh deploy.sh grafana + gala-gopher-aarch64:22.03-lts-sp1.tar + gala-gopher.conf + gala-gopher-app.conf + stackprobe.conf ``` +1. 工具一键部署 + +将gala_deploy_gopher目录下的所有文件和[部署脚本](./deploy/deploy.sh),[辅助脚本](./deploy/comm.sh) 上传到目标生产节点机器上,执行如下命令安装、配置、启动gala-gopher服务,-S选项来指定离线安装包所在的目录。 + +```xml +sh deploy.sh gopher -K -p -S <离线安装包所在目录> [--docker] +``` + +选项详细说明: + +| 选项 | 参数说明 | 是否必配 | +| :-------------: | :----------------------------------------------------------: | :------------: | +| -K\|--kafka | 指定gala-gopher上报采集数据的目标kakfa服务器地址,当不配置该选项时,kafka服务器地址使用localhost | 否 | +| -p\|--pyroscope | 指定gala-gopher开启火焰图功能后火焰图上传到的pyroscope服务器地址(用于对接前端界面显示),当不配置该选项时,pyroscope服务器地址使用localhost | 否 | +| -S\|--srcdir | 离线部署时使用该选项来指定gala-gopher以及其依赖包所在的目录 | 离线部署时必配 | +| --docker | 指定以 docker 方式部署 gala-gopher | 否 | + +#### 管理节点:部署gala-ops + +1. gala-ops容器镜像下载 + +离线部署前,需要在可连接外网的机器上下载gala-ops(gala-anteater/gala-spider/gala-inference)容器镜像tar包。本工具提供了[离线资源下载脚本](./deploy/download_offline_res.sh),[辅助脚本](./deploy/comm.sh)一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_ops下。 + +```xml +sh download_offline_res.sh ops [os_arch] +``` + +可选选项: + +- os_arch: 指定下载该架构的容器镜像。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64 + +2. 工具一键部署 + +将gala_deploy_ops目录下的所有文件和[部署脚本](./deploy/deploy.sh),[辅助脚本](./deploy/comm.sh) 上传到目标管理节点机器上,执行如下命令安装、配置、启动gala-ops服务,-S选项来指定容器镜像tar包所在的目录。 + +```shell +sh deploy.sh ops -K -P -A -S +``` + +选项详细说明: + +| 选项 | 参数说明 | 是否必配 | +| :--------------: | :----------------------------------------------------------: | :------------: | +| -K\|--kafka | 指定gala-ops读取消息的kakfa服务器地址,当不配置该选项时,kafka服务器地址使用localhost | 否 | +| -P\|--prometheus | 指定gala-ops读取消息的prometheus服务器地址,当不配置该选项时,prometheus服务器地址使用localhost | 否 | +| -A\|--arangodb | 指定gala-ops存储关系图数据的的arangodb服务器地址,当不配置该选项时,arangodb服务器地址使用localhost | 否 | +| -S\|--srcdir | 离线部署时使用该选项来指定gala-ops容器镜像tar包所在的目录 | 离线部署时必配 | + +#### 管理节点:部署grafana + +1. grafana容器镜像与依赖python库下载 + +离线部署前,需要在可连接外网的机器上下载grafana容器镜像与arangodb2es.py依赖的python库。本工具提供了[离线资源下载脚本](./deploy/download_offline_res.sh),[辅助脚本](./deploy/comm.sh)一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_grafana下: + +```xml +sh download_offline_res.sh grafana [os_arch] +``` + +可选选项: + +- os_arch: 指定下载该架构的容器镜像。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64 + +注:arangodb2es.py用于将arangodb中的图关系数据转换上传到elasticsearch服务器,以便支持grafana页面上显示拓扑图。* + +2. 工具一键部署 + +将gala_deploy_grafana下的所有文件、[arangodb2es.py](./deploy/arangodb2es.py)和[部署脚本](./deploy/deploy.sh),[辅助脚本](./deploy/comm.sh) 上传到目标管理节点,执行如下命令完成部署,grafana会以容器实例方式运行。 + +```xml +sh deploy.sh grafana -P -p -E -S +``` + +选项详细说明: + +| 选项 | 参数说明 | 是否必配 | +| :--------------: | :----------------------------------------------------------: | :------------: | +| -P\|--prometheus | 指定grafana中的prometheus数据源地址,当不配置该选项时,prometheus数据源使用localhost | 否 | +| -p\|--pyroscope | 指定grafana中读取火焰图的pyroscope数据源地址,当不配置该选项时,pyroscope数据源使用localhost | 否 | +| -E\|--elastic | 指定grafana中读取异常检测、拓扑图、根因定位结果的elasticsearch数据源地址。当不使用该选项时,elasticsearch数据源使用localhost | 否 | +| -S\|--srcdir | 离线部署时使用该选项来指定grafana安装包所在的目录 | 离线部署时必配 | + +### 在线部署 + +#### 获取部署脚本 + +下载单独的[部署脚本](./deploy/deploy.sh),[辅助脚本](./deploy/comm.sh) 无需下载整个工具,可以直接通过如下命令下载到待部署机器上: + +``` +wget https://gitee.com/openeuler/gala-docs/raw/master/deploy/deploy.sh --no-check-certificate +wget https://gitee.com/openeuler/gala-docs/raw/master/deploy/comm.sh --no-check-certificate +``` + +#### 管理节点:部署中间件 + +执行如下命令安装、配置、启动kafka/prometheus/elasticsearch/logstash/arangodb/pyroscope服务,**-K/-P/-E/-A/-p选项支持分开使用单独部署对应组件**,其中-P用于配置prometheus服务端抓取消息的来源地址(即部署gala-gopher的生产节点)列表,每个地址之间用英文逗号分隔;elasticsearch/logstash由于存在依赖关系,通过-E选项统一控制、绑定安装。 + +```css +sh deploy.sh middleware -K <部署节点管理IP> -P -E <部署节点管理IP> -A -p + +``` + +#### 生产节点:部署gala-gopher + +通过如下命令来安装、配置、启动gala-gopher服务: + +1. rpm方式 + +```xml +sh deploy.sh gopher -K -p +``` + +2. 容器镜像方式: + +```css +sh deploy.sh gopher -K -p --docker +``` + +#### 管理节点:部署gala-ops + +gala-ops组件支持rpm、容器镜像两种部署方式,部署时需要指定kafka、prometheus、arangodb服务器地址,当不指定时,这些中间件的地址默认使用localhost。 + +1. rpm方式(仅支持openEuler 22.03 LTS SP1) + +```shell +sh deploy.sh ops -K -P -A +``` + +2. 容器镜像方式: + +```css +sh deploy.sh ops -K -P -A --docker +``` + +#### 管理节点:部署grafana + +将[arangodb2es.py](./arangodb2es.py)下载上传到目标管理节点上,并和部署脚本放在同一个目录,执行如下命令完成前端页面部署,grafana会以容器实例方式运行。 + +```xml +sh deploy.sh grafana -P -E +``` + + + [gala-ops部署演示视频](https://gitee.com/openeuler/gala-docs/blob/master/demo/5.%20A-Ops%E7%BB%84%E4%BB%B6%E9%83%A8%E7%BD%B2.mp4)中以openEuler 22.03 LTS版本为例演示了使用部署工具完成在生成节点上的gala-gopher以及在管理节点上的gala-ops组件部署的过程。 {{< video ratio="16x9" src="/gala-docs/videos/5.A-Ops_deploy_tools.webm" >}} @@ -484,7 +715,7 @@ Google针对云服务SLI的评估提出VALET方法,从5个维度综合评估 -## 系统I/O全栈诊断 +## 系统I/O全栈观测 ### 特性背景 @@ -512,11 +743,11 @@ Google针对云服务SLI的评估提出VALET方法,从5个维度综合评估 [分布式存储I/O全栈诊断视频](https://gitee.com/openeuler/gala-docs/blob/master/demo/3.%20A-Ops%20%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8%E5%9C%BA%E6%99%AF%20%E2%80%93%20%E5%9C%A8%E7%BA%BF%E7%B3%BB%E7%BB%9FIO%E8%AF%8A%E6%96%AD.mp4) {{< video ratio="16x9" src="/gala-docs/videos/3.A-Ops_distributed_storage_io_stack.webm" >}} -## 系统隐患诊断 +## 精细化性能Profiling ### 特性背景 -用户在日常运维过程还经常会遇到僵尸进程、内存泄漏、CPU冲高等问题,这些问题现象表现在系统层面,但是问题根因常见在应用层面。为了能够让系统运维SRE快速定界问题范围,gala-ops提供系统隐患诊断能力(隐患是指应用对系统产生的不利影响),用于监控/诊断非健康应用对系统产生的持续性、随机性的隐患,包括CPU冲高、内存泄漏(或持续增长)、I/O带宽拥塞等。 +用户在日常运维过程还经常会遇到僵尸进程、内存泄漏、CPU冲高等问题,这些问题现象表现在系统层面,但是问题根因常见在应用层面。为了能够让系统运维SRE快速定界问题范围,gala-ops提供精细化性能Profiling能力,其支持长期、在线采集系统/应用性能数据,可以快速诊断包括CPU冲高、内存泄漏(或持续增长)、系统调用异常、资源不足等问题。 ### 解决方案 @@ -529,44 +760,16 @@ Google针对云服务SLI的评估提出VALET方法,从5个维度综合评估 - 采样负载评估:以10ms采样一次数据为例,一次采样逻辑的指令预估 1W条( CPU 10MS 指令数量大概能够执行 1KW条指令),采样指令数量/CPU单位时间执行数量 = 1W/1KW 0.1%,所以采样负载理论上是 0.1%(每核) - 数据存储评估:采样数据需保存一段时间用于周期性的转换成函数符号。假设采样频率10ms,转换周期1min,那么最少要保留的采样点:1min/10ms * 单次采样数据。即单核大约 6000个采样点。放大评估,单核大约1.2W个采样点。 +gala-ops支持使用Grafana图形界面用来帮助客户更好的理解性能Profiling结果,包括火焰图、时间线图两种形式。 + ### 案例演示 -[系统隐患诊断视频](https://gitee.com/openeuler/gala-docs/blob/master/demo/4.%20A-Ops%20%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9C%BA%E6%99%AF%20-%20%E7%B3%BB%E7%BB%9F%E9%9A%90%E6%82%A3%E8%AF%8A%E6%96%AD%EF%BC%88%E7%81%AB%E7%84%B0%E5%9B%BE%EF%BC%89.mp4) +[精细化性能Profiling视频](https://gitee.com/openeuler/gala-docs/blob/master/demo/4.%20A-Ops%20%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9C%BA%E6%99%AF%20-%20%E7%B3%BB%E7%BB%9F%E9%9A%90%E6%82%A3%E8%AF%8A%E6%96%AD%EF%BC%88%E7%81%AB%E7%84%B0%E5%9B%BE%EF%BC%89.mp4) {{< video ratio="16x9" src="/gala-docs/videos/4.A-Ops_datebase_flamegraph.webm">}} +# 待发布特性 -# 未来规划 - -### 背景介绍 - -云原生是云场景的发展趋势,越来越多的场景采取云原生方式部署业务。在云原生场景的运维软件目前也非常丰富,但是系统层面的运维在云原生场景依然存在一些问题。具体表现在: - -- 在云原生领域,现有成熟监控工具:cAdvisor、Atop、Ganglia等只能看到kernel暴露的数据,无法高保真的监控应用运行状态。 -- 传统监控APM在面临云原生基础设施厚重的背景下,存在无法深入基础软件内部、无法弹性/动态插桩、语言强依赖等问题。 -- 基础软件相关的观测工具也存在架构开放性不足(强依赖某种技术,比如istio),引入系统底噪(skywalk引起JVM savepoint)等问题。 -- 云原生应用的运行状态更多停留在内核中,观测离不开对kernel内运行状态洞察,虽然kernel有cgroup、namespace等抽象,但与云原生应用视角依然存在GAP。 - -总结:现有云原生观测技术存在语言依赖性、底噪高、弹性能力不足、全栈观测能力不足等问题。 - -![](./png/plan-1.png) - -### 问题及解决思路 - -以云原生常见的java应用为例,常见的java应用性能问题通常要经过四个步骤完成定位。过程(归纳示例)参考如下: - -![](./png/plan-3.png) - - - -详细步骤介绍如下: - -| 步骤 | 过程分析 | 存在问题 | 问题总结 | 解决方案 | -| :--- | ------------------------------------------------------------ | ------------------------------------------------------------ | --------------------------------- | ------------------------------------------------------------ | -| 1 | 通过APM进行集群内分布式跟踪,实现业务实例级定界(定位到某个容器实例) | 1. skywalking等传统apm存在底噪问题(影响应用吞吐量约10%);
2. 语言强相关性。 | 底噪大,侵入式修改。 | 无侵入分布式Tracing | -| 2 | 通过perf + AsyncProfier等工具实现容器实例内性能热点抓取,定位至某业务流程。 | 1. linux 、jvm性能数据分开采集,无法统筹分析;
2. perf等工具无法细粒度采集单个容器实例性能热点数据; |缺乏全栈细粒度性能数据采集能力|**全栈细粒度性能火焰图**:低底噪、实时全栈(覆盖Linux、JVM) OnCPU、OffCPU、内存热点火焰图| -| 3 | 通过业务专家分析业务流程,辅助日志、插桩等方式定位至具体函数。 | 1. 日志、插桩等方式存在效率低的问题(需要重新出版本);
2. 业务流程中的系统性能事件无法观测到(比如线程切换,锁操作,文件操作、网络时延等); |缺乏业务Request级性能Profling能力|**Request级性能Profiling**:提供在线的Request级性能事件观测能力(包括文件操作、网络访问、锁操作等)| -| 4 | 如果问题是出现在底层(比如慢I/O),则依赖业务/系统专家会诊,辅助各类工具。 | 1. 依赖人力会诊,效率低;
2. 随机性故障无法追溯; |缺乏下钻式全栈观测能力|细粒度下钻式全栈观测能力:提供全栈的应用(进程/线程)粒度系统性能数据,并提供应用/系统性能瓶颈分析能力。| - -备注:2/3解决方案是gala-ops在云原生场景未来规划的特性,4属于现有特性针对云原生场景的补充增强。 +1. 系统隐患巡检 +2. K8S Pod全栈可观测及诊断 # 常见问题 @@ -581,4 +784,6 @@ Google针对云服务SLI的评估提出VALET方法,从5个维度综合评估 # 用户案例 -# 合作厂商 \ No newline at end of file +# 合作厂商 + +![](./png/partner.png) \ No newline at end of file diff --git a/theme/public/404.html b/theme/public/404.html index 37de135b072f05f7e4f6199edc9236ea2a7d4524..77d3aa78104fd7a5de896598c3bc11eab5f96fd3 100644 --- a/theme/public/404.html +++ b/theme/public/404.html @@ -10,7 +10,7 @@ - + 404 Page not found - Gala @@ -235,10 +235,10 @@ - + - + diff --git a/theme/public/categories/index.html b/theme/public/categories/index.html index ac7fb7aa1d9237784852d45357494c569ca11a62..fb598d74dfcdb543f74833a3fa2f1a1a472bdee7 100644 --- a/theme/public/categories/index.html +++ b/theme/public/categories/index.html @@ -10,7 +10,7 @@ - + @@ -240,10 +240,10 @@ - + - + diff --git a/theme/public/contributors/index.html b/theme/public/contributors/index.html index 6a6ba97d5e96a82fc70c11f6ffdea6efc40b2a9e..7cd67bc8a138e4ec59bb9886309881380f8c8444 100644 --- a/theme/public/contributors/index.html +++ b/theme/public/contributors/index.html @@ -10,7 +10,7 @@ - + @@ -240,10 +240,10 @@ - + - + diff --git a/theme/public/docs/gala-docs/gala_event_api/index.html b/theme/public/docs/gala-docs/gala_event_api/index.html index b63f6beda0a9c5fd7dd4df9d78ae23415b52c26f..d44ffc8c33dd9fffecc35ce768e45917c96a0c57 100644 --- a/theme/public/docs/gala-docs/gala_event_api/index.html +++ b/theme/public/docs/gala-docs/gala_event_api/index.html @@ -1,436 +1,436 @@ - - - - - - - - - - - - - - - - - - gala_event_api - Gala - + + + + + + + + + + + + + + + + gala_event_api - Gala + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- -
- - -
-
- -
-
- -
- -
- -

gala_event_api

-

- +
+ + +
+ +

gala_event_api

+

+ +
+
+ + + - - -

异常检测&根因定位数据ArangoDB接口使用指导

+ + + + +

异常检测&根因定位数据ArangoDB接口使用指导

异常检测、根因定位结果数据默认输出到kafka中,由其它子系统订阅消费使用。本文档提供另外一种数据对接方式,指导将kafka中的数据实时同步到ArangoDB中,其它子系统可以直接使用ArangoDB的接口获取异常检测、根因定位的数据。

本数据对接方案的原理,通过开源数据采集工具logstash将kafka中的数据实时消费并转存到ArangoDB中。

-

1. ArangoDB环境准备

+

1. ArangoDB环境准备

ArangoDB的安装部署参考官网,详细过程略。下面介绍创建异常检测&根因定位对应的db和collection。

-

1.1 db创建

-
shell> curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_api/database <<EOF
-{ 
-    "name" : "gala_event", 
-    "options" : { 
-        "sharding" : "flexible", 
-        "replicationFactor" : 3 
-    } 
-}
-EOF
-
-HTTP/1.1 201 Created
-content-type: application/json
-connection: Keep-Alive
-content-length: 40
-server: ArangoDB
-x-arango-queue-time-seconds: 0.000000
-x-content-type-options: nosniff
-
-{ 
-"error" : false, 
-"code" : 201, 
-"result" : true 
-}
+

1.1 db创建

+
shell> curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_api/database <<EOF
+{ 
+    "name" : "gala_event", 
+    "options" : { 
+        "sharding" : "flexible", 
+        "replicationFactor" : 3 
+    } 
+}
+EOF
+
+HTTP/1.1 201 Created
+content-type: application/json
+connection: Keep-Alive
+content-length: 40
+server: ArangoDB
+x-arango-queue-time-seconds: 0.000000
+x-content-type-options: nosniff
+
+{ 
+"error" : false, 
+"code" : 201, 
+"result" : true 
+}
 
-

1.2 collection创建

-
shell> curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/collection <<EOF
-{ 
-    "name" : "gala_event_anteater" 
-}
-EOF
-
-HTTP/1.1 200 OK
-content-type: application/json
-connection: Keep-Alive
-content-length: 436
-server: ArangoDB
-x-arango-queue-time-seconds: 0.000000
-x-content-type-options: nosniff
-
-{ 
-    "error" : false, 
-    "code" : 200, 
-    "waitForSync" : false, 
-    "status" : 3, 
-    "globallyUniqueId" : "h1466B7768774/66935", 
-    "isSystem" : false, 
-    "internalValidatorType" : 0, 
-    "isSmartChild" : false, 
-    "id" : "66935", 
-    "name" : "gala_event_anteater", 
-    "type" : 2, 
-    "objectId" : "66934", 
-    "usesRevisionsAsDocumentIds" : true, 
-    "schema" : null, 
-    "writeConcern" : 1, 
-    "syncByRevision" : true, 
-    "cacheEnabled" : false, 
-    "keyOptions" : { 
-    "allowUserKeys" : true, 
-    "type" : "traditional", 
-    "lastValue" : 0 
-    }, 
-    "statusString" : "loaded" 
-}
-
+

1.2 collection创建

+
shell> curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/collection <<EOF
+{ 
+    "name" : "gala_event_anteater" 
+}
+EOF
+
+HTTP/1.1 200 OK
+content-type: application/json
+connection: Keep-Alive
+content-length: 436
+server: ArangoDB
+x-arango-queue-time-seconds: 0.000000
+x-content-type-options: nosniff
+
+{ 
+    "error" : false, 
+    "code" : 200, 
+    "waitForSync" : false, 
+    "status" : 3, 
+    "globallyUniqueId" : "h1466B7768774/66935", 
+    "isSystem" : false, 
+    "internalValidatorType" : 0, 
+    "isSmartChild" : false, 
+    "id" : "66935", 
+    "name" : "gala_event_anteater", 
+    "type" : 2, 
+    "objectId" : "66934", 
+    "usesRevisionsAsDocumentIds" : true, 
+    "schema" : null, 
+    "writeConcern" : 1, 
+    "syncByRevision" : true, 
+    "cacheEnabled" : false, 
+    "keyOptions" : { 
+    "allowUserKeys" : true, 
+    "type" : "traditional", 
+    "lastValue" : 0 
+    }, 
+    "statusString" : "loaded" 
+}
+
 
-

2. logstash安装配置

-

2.1 下载安装

+

2. logstash安装配置

+

2.1 下载安装

下载 logstash

-

2.2 配置

+

2.2 配置

在$logstash安装路径,$logstash/config目录下新建kafka_to_arangodb.conf文件,内容如下(其中ip地址、端口、topic等用实际信息替换):

-
input {
-    kafka {
-        bootstrap_servers => "ip:9092"
-        topics => ["gala_anteater"]
-        group_id => "gala_group"
-        client_id => "gala_client"
-        decorate_events => "true"
-    }
-}
-
-filter {
-    json {
-        source => "message"
-    }
-
-    date {
-        match => ["Timestamp", "UNIX_MS"]
-        target => "@timestamp"
-    }
-
-}
-
-output {
-    stdout { codec => rubydebug }
-
-    http {
-        http_method => "post"
-        url => "http://ip:8529/_db/gala_event/_api/document?collection=gala_event_anteater"
-        format => "json"
-    }
-}
+
input {
+    kafka {
+        bootstrap_servers => "ip:9092"
+        topics => ["gala_anteater"]
+        group_id => "gala_group"
+        client_id => "gala_client"
+        decorate_events => "true"
+    }
+}
+
+filter {
+    json {
+        source => "message"
+    }
+
+    date {
+        match => ["Timestamp", "UNIX_MS"]
+        target => "@timestamp"
+    }
+
+}
+
+output {
+    stdout { codec => rubydebug }
+
+    http {
+        http_method => "post"
+        url => "http://ip:8529/_db/gala_event/_api/document?collection=gala_event_anteater"
+        format => "json"
+    }
+}
 
-

2.3 启动运行

-
bin/logstash -f config/kafka_to_arangodb.conf
+

2.3 启动运行

+
bin/logstash -f config/kafka_to_arangodb.conf
 

或者以后台服务的方式运行:

-
bin/logstash -f config/kafka_to_arangodb.conf -d
+
bin/logstash -f config/kafka_to_arangodb.conf -d
 
-

3. 数据获取接口

-

3.1 接口描述

+

3. 数据获取接口

+

3.1 接口描述

Arangodb 提供的 AQL 语句查询接口,详细的 API 定义参见 arangodb 官方文档: AQL查询接口

-

3.2 请求方法

+

3.2 请求方法

POST /_api/cursor

-

3.3 输入参数

+

3.3 输入参数

请求体

query(string类型,必选):包含要执行的查询字符串,这里它的内容为 "FOR t IN gala_event_anteater LIMIT 10 RETURN t"

-

3.4 输出参数

+

3.4 输出参数

HTTP 201:请求成功时的响应码,返回内容包括,

  • error(boolean类型):发生错误时标记为 true
  • code(integer类型):HTTP 状态码
  • result(数组类型):返回内容,这里只返回一个元素,它的值为拓扑图的时间戳。
-

3.5 请求示例

-
curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/cursor <<EOF
-{
-         "query": "FOR t IN gala_event_anteater LIMIT 10 RETURN t"
-}
-EOF
-{
-  "result": [
-    {
-      "_key": "6127900",
-      "_id": "gala_event_anteater/6127900",
-      "_rev": "_epRnG5C---",
-      "@version": "1",
-      "tags": [
-        "_jsonparsefailure"
-      ],
-      "message": "",
-      "event": {
-        "original": ""
-      },
-      "@timestamp": "2022-08-19T07:02:27.302593Z"
-    },
-    {
-      "_key": "6127928",
-      "_id": "gala_event_anteater/6127928",
-      "_rev": "_epRo-1m---",
-      "Resource": {
-        "metrics": "gala_gopher_system_net_net_device_rx_drops"
-      },
-      "SeverityText": "WARN",
-      "@version": "1",
-      "SeverityNumber": 13,
-      "Attributes": {
-        "Entity ID": "4c739ef759c142c18e8c3c29fxxxx_system_net_enp2s2"
-      },
-      "Body": "Thu Aug 11 00:21:51 2022 WARN Entity(enp2s2) net device rx queue drops(13).",
-      "Timestamp": "1660892578591",
-      "@timestamp": "2022-08-19T07:02:58.591Z"
-    }
-  ],
-  "hasMore": false,
-  "cached": false,
-  "extra": {
-    "warnings": [],
-    "stats": {
-      "writesExecuted": 0,
-      "writesIgnored": 0,
-      "scannedFull": 2,
-      "scannedIndex": 0,
-      "filtered": 0,
-      "httpRequests": 0,
-      "executionTime": 0.0003535151481628418,
-      "peakMemoryUsage": 0
-    }
-  },
-  "error": false,
-  "code": 201
-}
+

3.5 请求示例

+
curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/cursor <<EOF
+{
+         "query": "FOR t IN gala_event_anteater LIMIT 10 RETURN t"
+}
+EOF
+{
+  "result": [
+    {
+      "_key": "6127900",
+      "_id": "gala_event_anteater/6127900",
+      "_rev": "_epRnG5C---",
+      "@version": "1",
+      "tags": [
+        "_jsonparsefailure"
+      ],
+      "message": "",
+      "event": {
+        "original": ""
+      },
+      "@timestamp": "2022-08-19T07:02:27.302593Z"
+    },
+    {
+      "_key": "6127928",
+      "_id": "gala_event_anteater/6127928",
+      "_rev": "_epRo-1m---",
+      "Resource": {
+        "metrics": "gala_gopher_system_net_net_device_rx_drops"
+      },
+      "SeverityText": "WARN",
+      "@version": "1",
+      "SeverityNumber": 13,
+      "Attributes": {
+        "Entity ID": "4c739ef759c142c18e8c3c29fxxxx_system_net_enp2s2"
+      },
+      "Body": "Thu Aug 11 00:21:51 2022 WARN Entity(enp2s2) net device rx queue drops(13).",
+      "Timestamp": "1660892578591",
+      "@timestamp": "2022-08-19T07:02:58.591Z"
+    }
+  ],
+  "hasMore": false,
+  "cached": false,
+  "extra": {
+    "warnings": [],
+    "stats": {
+      "writesExecuted": 0,
+      "writesIgnored": 0,
+      "scannedFull": 2,
+      "scannedIndex": 0,
+      "filtered": 0,
+      "httpRequests": 0,
+      "executionTime": 0.0003535151481628418,
+      "peakMemoryUsage": 0
+    }
+  },
+  "error": false,
+  "code": 201
+}
 
- - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/theme/public/docs/gala-docs/gopher_tech/index.html b/theme/public/docs/gala-docs/gopher_tech/index.html index e04f00181c21eb67cfd06ca8f052cd667d115305..0f687a83a13391d61b4e17ce16a2ce811926fcba 100644 --- a/theme/public/docs/gala-docs/gopher_tech/index.html +++ b/theme/public/docs/gala-docs/gopher_tech/index.html @@ -1,429 +1,429 @@ - - - - - - - - - - - - - - - - - - gopher_tech - Gala - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- -
- - -
-
- -
-
- -
- -
- -

gopher_tech

-

- -

TCP(entity_name:tcp_link)

+ + + + + + + + + + + + + + + + + + gopher_tech - Gala + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+ +
+ + +
+
+ +
+
+ +
+ +
+ +

gopher_tech

+

+ +

TCP(entity_name:tcp_link)

@@ -886,7 +886,7 @@
-

ENDPOINT

+

ENDPOINT

@@ -1069,7 +1069,7 @@
-

QDISC

+

QDISC

@@ -1164,7 +1164,7 @@
-

THREAD(entity_name:task)

+

THREAD(entity_name:task)

@@ -1219,7 +1219,7 @@
-

Process(entity_name:proc)

+

Process(entity_name:proc)

@@ -1810,7 +1810,7 @@
-

BLOCK

+

BLOCK

@@ -2041,7 +2041,7 @@
-

Container

+

Container

@@ -2504,7 +2504,7 @@
-

Redis SLI

+

Redis SLI

@@ -2599,7 +2599,7 @@
-

Postgre SLI

+

Postgre SLI

支持版本:openssl 1.1.1

@@ -2703,7 +2703,7 @@
-

HTTP SLI

+

HTTP SLI

待补充支持的版本

@@ -2799,7 +2799,7 @@
-

DISK

+

DISK

@@ -2902,7 +2902,7 @@
-

NIC

+

NIC

@@ -3037,7 +3037,7 @@
-

CPU

+

CPU

@@ -3164,7 +3164,7 @@
-

MEM

+

MEM

@@ -3275,7 +3275,7 @@
-

FS

+

FS

@@ -3378,7 +3378,7 @@
-

NET

+

NET

@@ -3457,7 +3457,7 @@
-

Host

+

Host

@@ -3528,7 +3528,7 @@
-

Dnsmasq(entity_name:dnsmasq_link)

+

Dnsmasq(entity_name:dnsmasq_link)

@@ -3575,7 +3575,7 @@
-

LVS(entity_name:ipvs_link)

+

LVS(entity_name:ipvs_link)

支持的软件版本:>=EulerOS 2.9,且加载ipvs KO

@@ -3655,7 +3655,7 @@
-

Nginx(entity_name:nginx_link)

+

Nginx(entity_name:nginx_link)

支持的软件版本:1.12.1

@@ -3727,7 +3727,7 @@
-

Haproxy(entity_name:haproxy_link)

+

Haproxy(entity_name:haproxy_link)

支持的软件版本:2.5-dev0

@@ -3799,7 +3799,7 @@
-

JVM(entity_name:jvm)

+

JVM(entity_name:jvm)

@@ -3863,7 +3863,7 @@ - + @@ -3879,7 +3879,7 @@ - + @@ -3911,7 +3911,7 @@ - + @@ -3927,7 +3927,7 @@ - + @@ -3967,7 +3967,7 @@ - + @@ -4023,7 +4023,7 @@ - + @@ -4055,7 +4055,7 @@ - + @@ -4063,7 +4063,7 @@ - + @@ -4071,14 +4071,14 @@ - +
process_cpu_seconds_total jvm_processgaugecounter s 进程已使用的CPU时间
classes_loaded_total jvm_classgaugecounter JVM自执行以来加载的类的总数量
threads_started_total jvm_threadgaugecounter JVM的已启动线程数
area jvm_memkeylabel JVM内存类型:heap/noheap
pool jvm_mem_poolkeylabel 内存池类型
pool jvm_buf_poolkeylabel 缓冲池类型
gc jvm_gckeylabel 垃圾回收器名字
gc_collection_seconds_count jvm_gcgaugesummary 给定的垃圾回收器已发生的GC总次数
gc_collection_seconds_sum jvm_gcgaugesummary s 在给定的垃圾回收器花费的总时间
-

kafka (entity_name: kafka)

+

kafka (entity_name: kafka)

@@ -4149,57 +4149,57 @@
- - - - - -
-
- -
-
- - - - - - - - - - - - + + + + + +
+
+ +
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/theme/public/docs/gala-docs/gopher_tech_abnormal/index.html b/theme/public/docs/gala-docs/gopher_tech_abnormal/index.html index 8b630a5eec69be6e738da9811062e47e8392b421..c66f743a0a0a00b8993cdef236b0c1dc1920eeee 100644 --- a/theme/public/docs/gala-docs/gopher_tech_abnormal/index.html +++ b/theme/public/docs/gala-docs/gopher_tech_abnormal/index.html @@ -1,440 +1,440 @@ - - - - - - - - - - - - - - - - - - gopher_tech_abnormal - Gala - + + + + + + + + + + + + + + + + gopher_tech_abnormal - Gala + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
- -
- - -
-
- -
-
- -
- -
- -

gopher_tech_abnormal

-

- +
+ + +
+ +

gopher_tech_abnormal

+

+ +
+
+ + + - - -

系统异常事件

-

简介

+ + + + +

系统异常事件

+

简介

目前,gala-gopher和gala-anteater均提供系统异常检测能力。其中gala-gopher支持用户在启动各个探针的时候,通过阈值(包括上下限)设置异常范围,探针会根据阈值判断某个指标是否异常,如果异常则上报异常事件;而gala-anteater是一款基于AI的操作系统异常检测平台,主要涵盖时序数据预处理、异常点发现、以及异常上报等功能,其能够提供更加准确地系统级异常检测能力。

gala-gopher和gala-anteater遵循统一的系统级异常数据格式规范,能够更好地兼容其他应用。

-

如何开启异常事件

+

如何开启异常事件

gala-gopher如何开启异常事件

  • gopher支持异常事件的探针参考支持的异常事件
  • @@ -595,9 +595,9 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
  • gala-gopher异常事件开关、阈值通过探针启动参数传递,探针启动参数参考这里
  • -

    gala-gopher支持的异常事件

    +

    gala-gopher支持的异常事件

    本章以观测实体(entity_name)的粒度来介绍其支持的异常事件。

    -

    SLI

    +

    SLI

    @@ -618,48 +618,48 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    输出格式

    -
    {
    -	"Timestamp": <timestamp>,
    -	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<fd>",  # tgid-应用进程号  fd-应用的socket文件描述符
    -	"Attributes": {
    -		"entity_id": "<machine_id>_<entity_name>_<tgid>_<fd>",
    -		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<fd>",
    -		"event_type": "sys"							 # sys-表示异常事件类型为系统级 
    -	},
    -	"Resource": {
    -		"metrics": "gala_gopher_sli_<event_name>"      # event_name-异常事件名,参考上表第一列
    -	},
    -	"SeverityText": "WARN",
    -	"SeverityNumber": 13,
    -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) Process(TID:<tgid>, CIP(<c_ip>:<c_port>), SIP(<s_ip>:<s_port>)) SLI(<cmd>:<rtt_nsec>) exceed the threshold." # entity_id-<tgid>_<fd>  rtt_nsec-响应时延,单位ns
    -}
    +

    输出格式

    +
    {
    +	"Timestamp": <timestamp>,
    +	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<fd>",  # tgid-应用进程号  fd-应用的socket文件描述符
    +	"Attributes": {
    +		"entity_id": "<machine_id>_<entity_name>_<tgid>_<fd>",
    +		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<fd>",
    +		"event_type": "sys"							 # sys-表示异常事件类型为系统级 
    +	},
    +	"Resource": {
    +		"metrics": "gala_gopher_sli_<event_name>"      # event_name-异常事件名,参考上表第一列
    +	},
    +	"SeverityText": "WARN",
    +	"SeverityNumber": 13,
    +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) Process(TID:<tgid>, CIP(<c_ip>:<c_port>), SIP(<s_ip>:<s_port>)) SLI(<cmd>:<rtt_nsec>) exceed the threshold." # entity_id-<tgid>_<fd>  rtt_nsec-响应时延,单位ns
    +}
     
    -

    输出示例

    +

    输出示例

    • -
      rtt_nsec
      +
      rtt_nsec

      sli探针监控并统计具体应用(如Redis)的响应时延,当监测到响应时延超过阈值时上报异常事件。用户需要在启动gala-gopher前手动通过-T参数设置阈值。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_sli_3739183_23",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_sli_3739183_23",
      -		"event_id": "1661593284000_e473b23xxx_sli_3739183_23",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_sli_rtt_nsec"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_23) Process(TID:3739183, CIP(9.82.194.97:48202), SIP(9.82.206.89:3742)) SLI(SET:48678) exceed the threshold."
      -    # 示例的事件信息包含了Redis应用的进程号、发起请求的客户端以及redis-server的IP和port、具体的请求命令和本次响应时延(单位ns)
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_sli_3739183_23",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_sli_3739183_23",
      +		"event_id": "1661593284000_e473b23xxx_sli_3739183_23",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_sli_rtt_nsec"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_23) Process(TID:3739183, CIP(9.82.194.97:48202), SIP(9.82.206.89:3742)) SLI(SET:48678) exceed the threshold."
      +    # 示例的事件信息包含了Redis应用的进程号、发起请求的客户端以及redis-server的IP和port、具体的请求命令和本次响应时延(单位ns)
      +}
       
    - + @@ -746,108 +746,108 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    注:输入参数为NA表示不需要外部输入阈值参数,内部实现是根据指标值是否为0判断异常与否。

    -

    输出格式

    -
    {
    -	"Timestamp": <timestamp>,
    -	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",  
    -    		           # role-客户端/服务端 c_ip/s_ip-对端IP/本地IP c_port/s_port-对端端口/本地端口 family-协议族如IPv4
    -	"Attributes": {
    -		"entity_id": "<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",
    -		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",
    -		"event_type": "sys"
    -	},
    -	"Resource": {
    -		"metrics": "gala_gopher_tcp_link_<event_name>"	# event_name-异常事件名,参见上表中第一列
    -	},
    -	"SeverityText": "WARN",
    -	"SeverityNumber": 13,
    -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) <descriptions>." # descriptions-事件信息,参见上表第二列 
    -}
    +

    输出格式

    +
    {
    +	"Timestamp": <timestamp>,
    +	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",  
    +    		           # role-客户端/服务端 c_ip/s_ip-对端IP/本地IP c_port/s_port-对端端口/本地端口 family-协议族如IPv4
    +	"Attributes": {
    +		"entity_id": "<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",
    +		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<role>_<c_ip>_<s_ip>_<c_port>_<s_port>_<family>",
    +		"event_type": "sys"
    +	},
    +	"Resource": {
    +		"metrics": "gala_gopher_tcp_link_<event_name>"	# event_name-异常事件名,参见上表中第一列
    +	},
    +	"SeverityText": "WARN",
    +	"SeverityNumber": 13,
    +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) <descriptions>." # descriptions-事件信息,参见上表第二列 
    +}
     
    -

    输出示例

    +

    输出示例

    • -
      tcp_oom
      +
      tcp_oom

      tcpprobe探针会记录TCP内存占用量超过了系统设定的最大值的次数,也即检测到该条TCP连接内存不足的次数。该异常事件的阈值不需要用户手动配置,默认为0,即只要检测到TCP内存不足则上报异常事件。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_tcp_link_tcp_oom"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP out of memory(1)."									# 句尾括号中“1”表示检测到oom的次数为1
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_tcp_link_tcp_oom"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP out of memory(1)."									# 句尾括号中“1”表示检测到oom的次数为1
      +}
       
    • -
      backlog_drops
      +
      backlog_drops

      tcpprobe探针会记录因backlog队列满导致的丢包次数,若判断该丢包次数大于用户设定的阈值则上报异常事件。用户需要在启动gala-gopher前通过-D参数设定该阈值的值,不设定该值则此项异常事件关闭。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_tcp_link_backlog_drops"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP backlog queue drops(3)."										# 示例中设定阈值为2,句尾括号中“3”表示检测到此类丢包的次数为3
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_tcp_link_backlog_drops"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP backlog queue drops(3)."										# 示例中设定阈值为2,句尾括号中“3”表示检测到此类丢包的次数为3
      +}
       
    • -
      filter_drops
      +
      filter_drops

      tcpprobe探针会记录经过过滤器filter丢弃的包数,当判断该丢包数大于用户设定的阈值则上报异常事件。用户需要在启动gala-gopher前通过-D参数设定该阈值的值,不设定该值则此项异常事件关闭。和backlog_drops异常事件共用同一个参数。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_tcp_link_filter_drops"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP filter drops(4)." 									# 句尾括号中“4”表示检测到此类丢包的次数为4
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_tcp_link_filter_drops"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP filter drops(4)." 									# 句尾括号中“4”表示检测到此类丢包的次数为4
      +}
       
    • -
      syn_srtt
      +
      syn_srtt

      tcpprobe会记录服务端接收到SYN请求后,从发送SYN/ACK到接收到客户端ACK经过的时间,若判断该建链时间大于用户设置的阈值则上报异常事件。用户需要在启动gala-gopher之前通过-T参数设置阈值。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_tcp_link_syn_srtt"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP connection establish timed out(10 us)."
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2", 
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_id": "1661593284000_e473b23xxx_tcp_link_3739183_0_9.82.194.97_9.82.206.89_78202_3742_2",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_tcp_link_syn_srtt"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_0_9.82.194.97_9.82.206.89_78202_3742_2) TCP connection establish timed out(10 us)."
      +}
       
    -

    ENDPOINT

    +

    ENDPOINT

    @@ -924,169 +924,169 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    输出格式

    -
    {
    -	"Timestamp": <timestamp>,
    -	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",  
    -    		                            # s_ip和s_port可能为空,具体参考示例  table_name-具体类型为listen|connect|bind|udp
    -	"Attributes": {
    -		"entity_id": "<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",
    -		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",
    -		"event_type": "sys"
    -	},
    -	"Resource": {
    -		"metrics": "gala_gopher_endpoint_<event_name>"	# event_name-异常事件名,参见上表中第一列
    -	},
    -	"SeverityText": "WARN",
    -	"SeverityNumber": 13,
    -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) <descriptions>." # descriptions-事件信息,参见上表第二列
    -														         # entity_id-<tgid>_<s_ip>_<s_port>_<table_name>
    -}
    +

    输出格式

    +
    {
    +	"Timestamp": <timestamp>,
    +	"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",  
    +    		                            # s_ip和s_port可能为空,具体参考示例  table_name-具体类型为listen|connect|bind|udp
    +	"Attributes": {
    +		"entity_id": "<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",
    +		"event_id": "<timestamp>_<machine_id>_<entity_name>_<tgid>_<s_ip>_<s_port>_<table_name>",
    +		"event_type": "sys"
    +	},
    +	"Resource": {
    +		"metrics": "gala_gopher_endpoint_<event_name>"	# event_name-异常事件名,参见上表中第一列
    +	},
    +	"SeverityText": "WARN",
    +	"SeverityNumber": 13,
    +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(<entity_id>) <descriptions>." # descriptions-事件信息,参见上表第二列
    +														         # entity_id-<tgid>_<s_ip>_<s_port>_<table_name>
    +}
     
    -

    输出示例

    +

    输出示例

    • -
      listendrop
      +
      listendrop

      当检测到TCP accept丢弃次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_listendrop"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP listen drops(1)." # 句尾括号中“1”即丢包数
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_listendrop"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP listen drops(1)." # 句尾括号中“1”即丢包数
      +}
       
    • -
      accept_overflow
      +
      accept_overflow

      当检测到TCP 全连接队列溢出的次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_accept_overflow"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP accept queue overflow(1)."
      -}                                                                                       # 句尾括号中“1”表示队列溢出次数
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_accept_overflow"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP accept queue overflow(1)."
      +}                                                                                       # 句尾括号中“1”表示队列溢出次数
       
    • -
      syn_overflow
      +
      syn_overflow

      当检测到TCP 半连接队列溢出的次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_syn_overflow"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP syn queue overflow(1)."
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_syn_overflow"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP syn queue overflow(1)."
      +}
       
    • -
      passive_open_failed
      +
      passive_open_failed

      当检测到TCP 被动发起的建链失败次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_passive_open_failed"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP passive open failed(1)."
      -}                                                                                           # 句尾括号中“1”表示失败次数
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_*_3742_listen",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_passive_open_failed"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_*_3742_listen) TCP passive open failed(1)."
      +}                                                                                           # 句尾括号中“1”表示失败次数
       
    • -
      active_open_failed
      +
      active_open_failed

      当检测到TCP 主动发起的建链失败次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_active_open_failed"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_connect) TCP active open failed(1)."
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_connect",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_active_open_failed"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_connect) TCP active open failed(1)."
      +}
       
    • -
      bind_rcv_drops
      +
      bind_rcv_drops

      当检测到UDP服务端接收失败的次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_bind_rcv_drops"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_bind) UDP(S) queue drops(1)."
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_bind",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_bind_rcv_drops"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_bind) UDP(S) queue drops(1)."
      +}
       
    • -
      udp_rcv_drops
      +
      udp_rcv_drops

      当检测到UDP客户端接收失败的次数大于0则上报异常事件。该异常事件的阈值不需要用户手动配置,默认为0。

      -
      {
      -	"Timestamp": 1661593284000,
      -	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      -	"Attributes": {
      -		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      -		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      -		"event_type": "sys"
      -	},
      -	"Resource": {
      -		"metrics": "gala_gopher_endpoint_udp_rcv_drops"
      -	},
      -	"SeverityText": "WARN",
      -	"SeverityNumber": 13,
      -	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_udp) UDP(C) queue drops(1)."
      -}
      +
      {
      +	"Timestamp": 1661593284000,
      +	"event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      +	"Attributes": {
      +		"entity_id": "e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      +		"     event_id": "1661593284000_e473b23xxx_endpoint_3739183_9.82.206.89_0_udp",
      +		"event_type": "sys"
      +	},
      +	"Resource": {
      +		"metrics": "gala_gopher_endpoint_udp_rcv_drops"
      +	},
      +	"SeverityText": "WARN",
      +	"SeverityNumber": 13,
      +	"Body": "Sat Aug 27 17:41:24 2022 WARN Entity(3739183_9.82.206.89_0_udp) UDP(C) queue drops(1)."
      +}
       
    -

    THREAD

    +

    THREAD

    @@ -1107,7 +1107,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    PROC

    +

    PROC

    @@ -1170,7 +1170,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    BLOCK

    +

    BLOCK

    @@ -1198,7 +1198,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    DISK

    +

    DISK

    @@ -1219,7 +1219,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    DF

    +

    DF

    @@ -1247,7 +1247,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    NIC

    +

    NIC

    @@ -1289,7 +1289,7 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    CPU

    +

    CPU

    @@ -1310,9 +1310,9 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns
    -

    gala-aneater 支持的异常事件

    +

    gala-aneater 支持的异常事件

    gala-anteater当前支持应用级系统级两类异常检测任务,并上报对应异常事件。同时,对于每个异常事件,定义了统一的异常事件格式。

    -

    异常事件格式

    +

    异常事件格式

    gala-aneater全部异常事件遵循一种的异常事件格式,格式如下:

    @@ -1356,45 +1356,45 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    例如,系统级磁盘异常事件格式:

    -
    {
    -    "Timestamp": 1670926603208,
    -    "Attributes": {
    -        "entity_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da_disk_dm-9",
    -        "event_id": "1670926603208_2c1c455d-24a5-897c-ea11-bc08f2d510da_disk_dm-9",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_disk_rspeed_kB",
    -        "labels": {
    -            "disk_name": "dm-9",
    -            "instance": "10.xxx.xxx.xxx:xx",
    -            "job": "ceph2-10.xxx.xxx.xxx",
    -            "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da"
    -        },
    -        "score": 0.6,
    -        "cause_metrics": [
    -            {
    -                "metric": "gala_gopher_disk_rareq",
    -                "labels": {
    -                    "disk_name": "dm-9",
    -                    "instance": "10.109.xxx.xxx:xx",
    -                    "job": "ceph2-10.109.xxx.xxx",
    -                    "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da"
    -                },
    -                "score": 5.03982338419878,
    -                "description": "The disk I/O await time performance deteriorates due to read saturation rise.(Disk = dm-9)"
    -            }
    -        ],
    -        "description": "Disk write await time is increasing!"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:16:43 2022 WARN, SYS may be impacting performance issues.",
    -}
    +
    {
    +    "Timestamp": 1670926603208,
    +    "Attributes": {
    +        "entity_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da_disk_dm-9",
    +        "event_id": "1670926603208_2c1c455d-24a5-897c-ea11-bc08f2d510da_disk_dm-9",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_disk_rspeed_kB",
    +        "labels": {
    +            "disk_name": "dm-9",
    +            "instance": "10.xxx.xxx.xxx:xx",
    +            "job": "ceph2-10.xxx.xxx.xxx",
    +            "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da"
    +        },
    +        "score": 0.6,
    +        "cause_metrics": [
    +            {
    +                "metric": "gala_gopher_disk_rareq",
    +                "labels": {
    +                    "disk_name": "dm-9",
    +                    "instance": "10.109.xxx.xxx:xx",
    +                    "job": "ceph2-10.109.xxx.xxx",
    +                    "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da"
    +                },
    +                "score": 5.03982338419878,
    +                "description": "The disk I/O await time performance deteriorates due to read saturation rise.(Disk = dm-9)"
    +            }
    +        ],
    +        "description": "Disk write await time is increasing!"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:16:43 2022 WARN, SYS may be impacting performance issues.",
    +}
     
    -

    应用级

    -

    1. SLI性能劣化

    +

    应用级

    +

    1. SLI性能劣化

    gala-anteater应用级异常事件,主要基于应用级SLI指标进行异常检测,当前使用的sli指标为。

    @@ -1421,67 +1421,67 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926723208,
    -    "Attributes": {
    -        "entity_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f_sli_2866035_14566_POSTGRE_0",
    -        "event_id": "1670926723208_b59927a6-bce5-4f0a-a36e-18895ad59e6f_sli_2866035_14566_POSTGRE_0",
    -        "event_type": "app",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_sli_tps",
    -        "labels": {
    -            "app": "POSTGRE",
    -            "datname": "postgres",
    -            "ins_id": "14566",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "vm05-10.xxx.xxx.xxx",
    -            "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    -            "method": "0",
    -            "server_ip": "172.xxx.xxx.xxx",
    -            "server_port": "5432",
    -            "tgid": "2866035"
    -        },
    -        "score": 0.36,
    -        "cause_metrics": [
    -            {
    -                "metric": "gala_gopher_block_latency_req_last",
    -                "labels": {
    -                    "blk_name": "vdb",
    -                    "disk_name": "vdb",
    -                    "first_minor": "16",
    -                    "instance": "10.xxx.xxx.xxx:8888",
    -                    "job": "vm05-10.xxx.xxx.xxx",
    -                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    -                    "major": "253"
    -                },
    -                "score": 12.84235139857697,
    -                "description": "block层request时延最近值异常"
    -            },
    -            {
    -                "metric": "gala_gopher_proc_ns_ext4_flush",
    -                "labels": {
    -                    "comm": "gaussdb",
    -                    "instance": "10.xxx.xxx.xxx:8888",
    -                    "job": "vm05-10.xxx.xxx.xxx",
    -                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    -                    "tgid": "2866035"
    -                },
    -                "score": 7.933597165856872,
    -                "description": "ext4文件系统flush操作时间(单位ns)异常"
    -            }
    -        ],
    -        "description": "sli tps 异常"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:18:43 2022 WARN, APP may be impacting sli performance issues."
    -}
    +
    {
    +    "Timestamp": 1670926723208,
    +    "Attributes": {
    +        "entity_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f_sli_2866035_14566_POSTGRE_0",
    +        "event_id": "1670926723208_b59927a6-bce5-4f0a-a36e-18895ad59e6f_sli_2866035_14566_POSTGRE_0",
    +        "event_type": "app",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_sli_tps",
    +        "labels": {
    +            "app": "POSTGRE",
    +            "datname": "postgres",
    +            "ins_id": "14566",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "vm05-10.xxx.xxx.xxx",
    +            "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    +            "method": "0",
    +            "server_ip": "172.xxx.xxx.xxx",
    +            "server_port": "5432",
    +            "tgid": "2866035"
    +        },
    +        "score": 0.36,
    +        "cause_metrics": [
    +            {
    +                "metric": "gala_gopher_block_latency_req_last",
    +                "labels": {
    +                    "blk_name": "vdb",
    +                    "disk_name": "vdb",
    +                    "first_minor": "16",
    +                    "instance": "10.xxx.xxx.xxx:8888",
    +                    "job": "vm05-10.xxx.xxx.xxx",
    +                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    +                    "major": "253"
    +                },
    +                "score": 12.84235139857697,
    +                "description": "block层request时延最近值异常"
    +            },
    +            {
    +                "metric": "gala_gopher_proc_ns_ext4_flush",
    +                "labels": {
    +                    "comm": "gaussdb",
    +                    "instance": "10.xxx.xxx.xxx:8888",
    +                    "job": "vm05-10.xxx.xxx.xxx",
    +                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    +                    "tgid": "2866035"
    +                },
    +                "score": 7.933597165856872,
    +                "description": "ext4文件系统flush操作时间(单位ns)异常"
    +            }
    +        ],
    +        "description": "sli tps 异常"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:18:43 2022 WARN, APP may be impacting sli performance issues."
    +}
     
    -

    系统级

    +

    系统级

    gala-anteater系统级异常事件,主要基于系统级相关性能指标,进行异常检测,并上报异常事件。目前,主要的系统级异常事件有如下几类。

    -

    1. TCP建链性能劣化

    +

    1. TCP建链性能劣化

    上报TCP建链性能相关指标发生劣化,主要为:

    @@ -1502,42 +1502,42 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926603208,
    -    "Attributes": {
    -        "entity_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3530555_0_10.xxx.xxx.xxx_192.xxx.xxx.xxx_0_22_2",
    -        "event_id": "1670926603208_6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3530555_0_10.xxx.xxx.xxx_192.xxx.xxx.xxx_0_22_2",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_tcp_link_syn_srtt",
    -        "labels": {
    -            "client_ip": "10.xxx.xxx.xxx",
    -            "client_port": "0",
    -            "comm": "sshd",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "vm03-10.xxx.xxx.xxx",
    -            "machine_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e",
    -            "protocol": "2",
    -            "role": "0",
    -            "server_ip": "192.xxx.xxx.xxx",
    -            "server_port": "22",
    -            "tgid": "3530555"
    -        },
    -        "score": 0,
    -        "cause_metrics": [],
    -        "description": "RTT of syn packet(us): the max syn packets rtt is 29714 us"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:16:43 2022 WARN, SYS may be impacting performance issues.",
    -    "cause_metric": {
    -        "description": "Unknown"
    -    }
    -}
    +
    {
    +    "Timestamp": 1670926603208,
    +    "Attributes": {
    +        "entity_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3530555_0_10.xxx.xxx.xxx_192.xxx.xxx.xxx_0_22_2",
    +        "event_id": "1670926603208_6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3530555_0_10.xxx.xxx.xxx_192.xxx.xxx.xxx_0_22_2",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_tcp_link_syn_srtt",
    +        "labels": {
    +            "client_ip": "10.xxx.xxx.xxx",
    +            "client_port": "0",
    +            "comm": "sshd",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "vm03-10.xxx.xxx.xxx",
    +            "machine_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e",
    +            "protocol": "2",
    +            "role": "0",
    +            "server_ip": "192.xxx.xxx.xxx",
    +            "server_port": "22",
    +            "tgid": "3530555"
    +        },
    +        "score": 0,
    +        "cause_metrics": [],
    +        "description": "RTT of syn packet(us): the max syn packets rtt is 29714 us"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:16:43 2022 WARN, SYS may be impacting performance issues.",
    +    "cause_metric": {
    +        "description": "Unknown"
    +    }
    +}
     
    -

    2. TCP传输性能劣化

    +

    2. TCP传输性能劣化

    上报TCP传输性能相关指标发生劣化,主要为:

    @@ -1558,39 +1558,39 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926843208,
    -    "Attributes": {
    -        "entity_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3260713_0_10.xxx.xxx.xxx_172.xxx.xxx.xxx_0_5432_2",
    -        "event_id": "1670926843208_6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3260713_0_10.xxx.xxx.xxx_172.xxx.xxx.xxx_0_5432_2",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_tcp_link_srtt",
    -        "labels": {
    -            "client_ip": "10.xxx.xxx.xxx",
    -            "client_port": "0",
    -            "comm": "worker",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "vm03-10.xxx.xxx.xxx",
    -            "machine_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e",
    -            "protocol": "2",
    -            "role": "0",
    -            "server_ip": "172.xxx.xxx.xxx",
    -            "server_port": "5432",
    -            "tgid": "3260713"
    -        },
    -        "score": 0.72,
    -        "cause_metrics": [],
    -        "description": "Smoothed Round Trip Time(us)"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:20:43 2022 WARN, SYS may be impacting performance issues.",
    -}
    +
    {
    +    "Timestamp": 1670926843208,
    +    "Attributes": {
    +        "entity_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3260713_0_10.xxx.xxx.xxx_172.xxx.xxx.xxx_0_5432_2",
    +        "event_id": "1670926843208_6489d66f-6fb7-4c31-8f52-bf349d9bd63e_tcp_link_3260713_0_10.xxx.xxx.xxx_172.xxx.xxx.xxx_0_5432_2",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_tcp_link_srtt",
    +        "labels": {
    +            "client_ip": "10.xxx.xxx.xxx",
    +            "client_port": "0",
    +            "comm": "worker",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "vm03-10.xxx.xxx.xxx",
    +            "machine_id": "6489d66f-6fb7-4c31-8f52-bf349d9bd63e",
    +            "protocol": "2",
    +            "role": "0",
    +            "server_ip": "172.xxx.xxx.xxx",
    +            "server_port": "5432",
    +            "tgid": "3260713"
    +        },
    +        "score": 0.72,
    +        "cause_metrics": [],
    +        "description": "Smoothed Round Trip Time(us)"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:20:43 2022 WARN, SYS may be impacting performance issues.",
    +}
     
    -

    3. 系统I/O性能劣化

    +

    3. 系统I/O性能劣化

    上报系统I/O性能相关指标发生劣化,主要为:

    @@ -1611,50 +1611,50 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926783208,
    -    "Attributes": {
    -        "entity_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da_block_8_80",
    -        "event_id": "1670926783208_2c1c455d-24a5-897c-ea11-bc08f2d510da_block_8_80",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_block_latency_req_max",
    -        "labels": {
    -            "blk_name": "sdf",
    -            "disk_name": "sdf",
    -            "first_minor": "80",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "ceph2-10.xxx.xxx.xxx",
    -            "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da",
    -            "major": "8"
    -        },
    -        "score": 0.36,
    -        "cause_metrics": [
    -            {
    -                "metric": "gala_gopher_block_read_bytes",
    -                "labels": {
    -                    "blk_name": "sdf",
    -                    "disk_name": "sdf",
    -                    "first_minor": "80",
    -                    "instance": "10.xxx.xxx.xxx:8888",
    -                    "job": "ceph2-10.xxx.xxx.xxx",
    -                    "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da",
    -                    "major": "8"
    -                },
    -                "score": 6.665321050421419,
    -                "description": "System performance deteriorates due to frequent read I/O operations.(Disk = sdf)"
    -            }
    -        ],
    -        "description": "Block I/O latency performance is deteriorating!"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:19:43 2022 WARN, SYS may be impacting performance issues."
    -}
    +
    {
    +    "Timestamp": 1670926783208,
    +    "Attributes": {
    +        "entity_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da_block_8_80",
    +        "event_id": "1670926783208_2c1c455d-24a5-897c-ea11-bc08f2d510da_block_8_80",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_block_latency_req_max",
    +        "labels": {
    +            "blk_name": "sdf",
    +            "disk_name": "sdf",
    +            "first_minor": "80",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "ceph2-10.xxx.xxx.xxx",
    +            "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da",
    +            "major": "8"
    +        },
    +        "score": 0.36,
    +        "cause_metrics": [
    +            {
    +                "metric": "gala_gopher_block_read_bytes",
    +                "labels": {
    +                    "blk_name": "sdf",
    +                    "disk_name": "sdf",
    +                    "first_minor": "80",
    +                    "instance": "10.xxx.xxx.xxx:8888",
    +                    "job": "ceph2-10.xxx.xxx.xxx",
    +                    "machine_id": "2c1c455d-24a5-897c-ea11-bc08f2d510da",
    +                    "major": "8"
    +                },
    +                "score": 6.665321050421419,
    +                "description": "System performance deteriorates due to frequent read I/O operations.(Disk = sdf)"
    +            }
    +        ],
    +        "description": "Block I/O latency performance is deteriorating!"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:19:43 2022 WARN, SYS may be impacting performance issues."
    +}
     
    -

    4. 进程I/O性能劣化

    +

    4. 进程I/O性能劣化

    上报进程I/O性能相关指标发生劣化,主要为:

    @@ -1699,46 +1699,46 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926783208,
    -    "Attributes": {
    -        "entity_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f_proc_2869374",
    -        "event_id": "1670926783208_b59927a6-bce5-4f0a-a36e-18895ad59e6f_proc_2869374",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_proc_greater_4k_io_read",
    -        "labels": {
    -            "comm": "gaussdb",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "vm05-10.xxx.xxx.xxx",
    -            "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    -            "tgid": "2869374"
    -        },
    -        "score": 0.52,
    -        "cause_metrics": [
    -            {
    -                "metric": "gala_gopher_proc_less_4k_io_read",
    -                "labels": {
    -                    "comm": "gaussdb",
    -                    "instance": "10.xxx.xxx.xxx:8888",
    -                    "job": "vm05-10.xxx.xxx.xxx",
    -                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    -                    "tgid": "2869374"
    -                },
    -                "score": 2.4876154256202576,
    -                "description": "System performance degrades due to frequent small I/O read operations.(Disk = , PID = 2869374, comm = gaussdb)"
    -            }
    -        ],
    -        "description": "Number of big I/O (greater than 4 KB) read operations at the BIO layer."
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:19:43 2022 WARN, SYS may be impacting performance issues."
    -}
    +
    {
    +    "Timestamp": 1670926783208,
    +    "Attributes": {
    +        "entity_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f_proc_2869374",
    +        "event_id": "1670926783208_b59927a6-bce5-4f0a-a36e-18895ad59e6f_proc_2869374",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_proc_greater_4k_io_read",
    +        "labels": {
    +            "comm": "gaussdb",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "vm05-10.xxx.xxx.xxx",
    +            "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    +            "tgid": "2869374"
    +        },
    +        "score": 0.52,
    +        "cause_metrics": [
    +            {
    +                "metric": "gala_gopher_proc_less_4k_io_read",
    +                "labels": {
    +                    "comm": "gaussdb",
    +                    "instance": "10.xxx.xxx.xxx:8888",
    +                    "job": "vm05-10.xxx.xxx.xxx",
    +                    "machine_id": "b59927a6-bce5-4f0a-a36e-18895ad59e6f",
    +                    "tgid": "2869374"
    +                },
    +                "score": 2.4876154256202576,
    +                "description": "System performance degrades due to frequent small I/O read operations.(Disk = , PID = 2869374, comm = gaussdb)"
    +            }
    +        ],
    +        "description": "Number of big I/O (greater than 4 KB) read operations at the BIO layer."
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:19:43 2022 WARN, SYS may be impacting performance issues."
    +}
     
    -

    5. 磁盘读写时延性能劣化

    +

    5. 磁盘读写时延性能劣化

    上报磁盘读写时延性能相关指标发生劣化,主要为:

    @@ -1765,87 +1765,87 @@ SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_ns

    输出示例:

    -
    {
    -    "Timestamp": 1670926723208,
    -    "Attributes": {
    -        "entity_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9_disk_sdc",
    -        "event_id": "1670926723208_2cd02d44-24a5-a12b-ea11-610b749c10a9_disk_sdc",
    -        "event_type": "sys",
    -        "event_source": "gala-anteater"
    -    },
    -    "Resource": {
    -        "metric": "gala_gopher_disk_r_await",
    -        "labels": {
    -            "disk_name": "sdc",
    -            "instance": "10.xxx.xxx.xxx:8888",
    -            "job": "ceph1-10.xxx.xxx.xxx",
    -            "machine_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9"
    -        },
    -        "score": 0.32,
    -        "cause_metrics": [
    -            {
    -                "metric": "gala_gopher_disk_wareq",
    -                "labels": {
    -                    "disk_name": "sdc",
    -                    "instance": "10.xxx.xxx.xxx:8888",
    -                    "job": "ceph1-10.xxx.xxx.xxx",
    -                    "machine_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9"
    -                },
    -                "score": 7.573942408085913,
    -                "description": "The disk I/O await time performance deteriorates due to write saturation rise.(Disk = sdc)"
    -            }
    -        ],
    -        "description": "Disk read await time is increasing!"
    -    },
    -    "SeverityText": "WARN",
    -    "SeverityNumber": 13,
    -    "Body": "Tue Dec 13 18:18:43 2022 WARN, SYS may be impacting performance issues."
    -}
    +
    {
    +    "Timestamp": 1670926723208,
    +    "Attributes": {
    +        "entity_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9_disk_sdc",
    +        "event_id": "1670926723208_2cd02d44-24a5-a12b-ea11-610b749c10a9_disk_sdc",
    +        "event_type": "sys",
    +        "event_source": "gala-anteater"
    +    },
    +    "Resource": {
    +        "metric": "gala_gopher_disk_r_await",
    +        "labels": {
    +            "disk_name": "sdc",
    +            "instance": "10.xxx.xxx.xxx:8888",
    +            "job": "ceph1-10.xxx.xxx.xxx",
    +            "machine_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9"
    +        },
    +        "score": 0.32,
    +        "cause_metrics": [
    +            {
    +                "metric": "gala_gopher_disk_wareq",
    +                "labels": {
    +                    "disk_name": "sdc",
    +                    "instance": "10.xxx.xxx.xxx:8888",
    +                    "job": "ceph1-10.xxx.xxx.xxx",
    +                    "machine_id": "2cd02d44-24a5-a12b-ea11-610b749c10a9"
    +                },
    +                "score": 7.573942408085913,
    +                "description": "The disk I/O await time performance deteriorates due to write saturation rise.(Disk = sdc)"
    +            }
    +        ],
    +        "description": "Disk read await time is increasing!"
    +    },
    +    "SeverityText": "WARN",
    +    "SeverityNumber": 13,
    +    "Body": "Tue Dec 13 18:18:43 2022 WARN, SYS may be impacting performance issues."
    +}
     
    - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/theme/public/docs/gala-docs/index.html b/theme/public/docs/gala-docs/index.html index bed9940e305dbc4b315be3ac56e9efc8f9fae2ef..4b8af8e00220b6f929c5dea50ce9a3ac10232672 100644 --- a/theme/public/docs/gala-docs/index.html +++ b/theme/public/docs/gala-docs/index.html @@ -10,7 +10,7 @@ - + @@ -386,10 +386,10 @@ - + - + diff --git a/theme/public/docs/gala-docs/index.xml b/theme/public/docs/gala-docs/index.xml index 7cc4e83ec7b48a45d54488cdbb9392d89877049e..9d072f894e2bf1e85b9df83986a36106ef2dc36f 100644 --- a/theme/public/docs/gala-docs/index.xml +++ b/theme/public/docs/gala-docs/index.xml @@ -12,25 +12,23 @@ Mon, 01 Jan 0001 00:00:00 +0000 https://openeuler.gitee.io/gala-docs/docs/gala-docs/introducation/ - 背景 # ​ 云场景中基础软件/业务应用之间的边界逐渐上移,基础软件逐渐成为云场景最重要的组成部分,而操作系统又最重要的基础软件之一。 ​ 从业界公开的数据看,云场景的一些重要故障均是与基础软件密切相关。公开数据显示现有主流云厂商月平均故障150+次数,75%的故障&lt;1H,90%&lt;1.5H,少量故障&gt;5H。 -云场景的基础设施、业务场景的复杂性,导致这些故障现象大量集中基础软件(尤其是操作系统)层面,为此openEuler社区规划&amp;孵化A-Ops项目,该项目包括基础设施监控、应用性能监控、应用安全、自动化及监控四大块功能。 -介绍 # ​ 针对云场景的故障特点,根据故障发展阶段划分成:系统隐患、灰度故障、故障 三个阶段,A-Ops规划应用性能监控解决方案,该解决方案包括多个关键组件,本文用于介绍相关gala-ops系列组件。 -​ gala-ops系列组件定位:云基础设施场景中,针对基础设施灰度故障导致的应用性能劣化、卡顿系统级故障在线诊断。提供包括应用性能诊断、系统性能瓶颈诊断、系统参数修复、系统实时拓扑等特性。 -原理 # 通过eBPF技术实现系统白盒化智能观测,实时在线完成系统架构拓扑化,在此基础完成从基础软硬件至应用现象的根因推导过程,且过程可视化。 -三步骤如下: -快速安装 # 架构 # gala-ops是C/S架构,可以集群方式部署,也可以单机部署。整个架构由gala-gopher、gala-ops两个软件组成,在集群模式下,gala-gopher安装在生产节点内,gala-ops安装在管理面节点内;单机模式两者均安装在生产节点内。 + 介绍 # gala-ops是一款C/S架构、基于AI的操作系统亚健康诊断工具。其基于eBPF + java agent无侵入观测技术,并以AI技术辅助,实现亚健康故障(比如性能抖动、错误率提升、系统卡顿等问题现象)分钟级诊断,简化IT基础设施的运维过程。 +背景 # 云基础设施在近几年随着云原生、无服务化等技术的实施,其运维的复杂性变得越来越有挑战性,尤其是亚健康问题特点(间歇性出现、持续时间短、问题种类多、涉及范围广等)给云基础设施故障诊断带来重要挑战。亚健康故障诊断的挑战(包括可观测能力、海量数据管理能力、AI算法的泛化能力等)在Linux场景中变的尤为突出。在openEuler开源操作系统中,现有的运维手段不足以及时发现、定位亚健康问题,存在包括:缺乏在线、持续性监控能力;缺乏应用视角精细化的观测能力;缺乏基于全栈观测数据的自动化、AI分析能力等问题。然而,针对亚健康故障的诊断能力其难点包括: +全栈的无侵入可观测观测能力。 持续、精细化、低负载的监控能力。 自适应不同应用场景的异常检测、可视化故障推导能力。 项目简介 # gala-ops的整体架构如图所示,其整体上是一个C/S架构。在生产节点gala-gopher是一个Linux后台程序,其负责提供全场景、全栈(包括Metrics、Events、Tracing等)的数据采集,其支持通过OpenTelemetry开放生态接口(支持prometheus exporter、kafka client等)将数据传递给管理节点。管理节点部署gala-spider、gala-anteater组件,分别负责集群拓扑计算、可视化根因推导; +gala-ops架构上依赖一些开源中间件(包括prometheus、kafka、Elastic等),但亦可对接至客户IT系统现有的中间件。gala-ops架构设计提供被集成能力,可以由行业客户IT运维系统集成。其提供两类被集成方式: +软件生态集成方式:可以只使用gala-gopher可观测能力(OpenTelemetry方式获取数据),亦可以使用全部能力,通过prometheus、Elastic、kafka等中间件获取观测数据、异常检测结果、可视化推导结果。 工具集成方式:将gala-ops提供的能力以Grafana形式集成至客户IT运维系统内。 gala-ops可以给客户提供如下运维能力: +在线应用性能抖动诊断:提供数据库类应用性能在线诊断能力,包括网络类(丢包、重传、时延、TCP零窗等)问题、I/O类(磁盘慢盘、I/O性能下降等)问题,调度类(包括sysCPU冲高、死锁等)问题、内存类(OOM、泄漏等)问题等。 系统性能瓶颈诊断:提供通用场景的TCP、I/O性能抖动问题诊断能力。 系统隐患巡检:提供内核协议栈丢包、虚拟化网络丢包、TCP异常、I/O时延异常、系统调用异常、资源泄漏、JVM异常、应用RPC异常(包括8种常见协议的错误率、时延等)硬件故障(UCE、磁盘介质错误等)等秒级巡检能力。 系统全栈I/O可观测:提供面向分布式存储场景的I/O全栈观测能力,包括GuestOS 进程级、Block层的I/O观测能力,以及虚拟化层存储前端I/O观测能力,分布式存储后端I/O观测能力。 精细化性能Profiling:提供多维度(包括系统、进程、容器、Pod等多个维度)、高精度(10ms采样周期)的性能(包括CPU性能、内存占用、资源占用、系统调用等类型)火焰图、时间线图,可实时在线持续性采集。 K8S Pod全栈可观测及诊断:提供K8S视角的Pod集群业务流实时拓扑能力,Pod性能观测能力、DNS观测能力、SQL观测能力等。 gala-ops涉及的关键技术包括如下: +融合型非侵入观测技术:融合eBPF、Java agent等不同观测技术优点,实现多语言(支持C/C++、Java、Go等主流语言)、全软件栈(包括内核、系统调用、基础库Glibc、运行时jvm、基础中间件Nginx/Haproxy等)的观测能力。 流程拓扑:基于时序化数据(L4/L7层流量等),实时计算生成时序化拓扑结构,动态展现业务集群拓扑变化。 可视化根因定位:统计推理模型结合全流程拓扑,实现可视化&amp;分钟级的问题根因诊断。 应用场景 # ​ gala-ops在openEuler等Linux环境主要面向场景包括数据库、分布式存储、虚拟化、云原生等场景。助力金融、电信、互联网等行业客户在全栈可观测的基础上实现亚健康故障分钟级诊断。 +项目代码仓 # https://gitee.com/openeuler/gala-gopher +https://gitee.com/openeuler/gala-spider +https://gitee.com/openeuler/gala-anteater +快速安装 # 架构 # gala-ops是C/S架构,可以集群方式部署,也可以单机部署。整个架构由gala-gopher、gala-ops两个软件组成,在集群模式下,gala-gopher安装在生产节点内,gala-ops安装在管理面节点内;单机模式两者均安装在生产节点内。 其中,gala-ops软件内包括gala-spider、gala-anteater、gala-inference组件。 -gala-gopher # 定位 # 数据采集器:提供应用粒度low-level的数据采集,包括网络、磁盘I/O、调度、内存、安全等方面的系统指标采集,同时负责应用KPI数据的采集。数据类型包括logging、tracing、metrics。 系统异常检测:提供系统异常检测能力,覆盖网络、磁盘I/O、调度、内存等方面的场景系统异常,用户可以通过阈值设置异常上下限范围。 性能热点分析:提供CPU、内存、IO火焰图。 原理及术语 # gala-gopher软件架构参考这里,其是一款基于eBPF技术的低负载探针框架,除了其自身采集的数据外,用户可以自由扩展第三方探针。 +gala-gopher # 定位 # 数据采集器:提供应用粒度low-level的数据采集,包括网络、磁盘I/O、调度、内存、安全等方面的系统指标采集,同时负责应用KPI数据的采集。数据类型包括logging、tracing、metrics。 系统异常检测:提供系统异常检测能力,覆盖网络、磁盘I/O、调度、内存等方面的场景系统异常,用户可以通过阈值设置异常上下限范围。 性能热点分析:提供CPU、内存、IO火焰图。 原理及术语 # gala-gopher软件架构参考这里,其是一款基于eBPF技术的低负载探针框架,除了其自身采集的数据外,用户可以自由扩展第三方探针。 术语 -探针:gala-gopher内执行具体数据采集任务的程序,包括native、extend 两类探针,前者以线程方式单独启动数据采集任务,后者以子进程方式启动数据采集任务。gala-gopher可以通过配置修改的方式启动部分或全部探针。 观测实体(entity_name):用来定义系统内的观测对象,所有探针采集的数据均会归属到具体的某个观测实体。每种观测实体均有key、label(可选)、metrics组成,比如tcp_link观测实体的key包括进程号、IP五元组、协议族等信息,metrics则包括tx、rx、rtt等运行状态指标。除原生支持的观测实体,gala-gopher也可以扩展观测实体。 数据表(table_name):观测实体由1张或更多数据表组合而成,通常1张数据表由1个采集任务完成,由此可知单个观测实体可以由多个采集任务共同完成。 meta文件:通过文件定义观测实体(包括内部的数据表),系统内meta文件必须保证唯一,定义不可冲突。规范参考这里。 支持的技术 # 采集范围:参考这里。覆盖网络、I/O、内存、网卡、调度、Redis、kafka、Nginx等内核及基础软件的RED(Request、Error、Delay)数据观测。 +探针:gala-gopher内执行具体数据采集任务的程序,包括native、extend 两类探针,前者以线程方式单独启动数据采集任务,后者以子进程方式启动数据采集任务。gala-gopher可以通过配置修改的方式启动部分或全部探针。 观测实体(entity_name):用来定义系统内的观测对象,所有探针采集的数据均会归属到具体的某个观测实体。每种观测实体均有key、label(可选)、metrics组成,比如tcp_link观测实体的key包括进程号、IP五元组、协议族等信息,metrics则包括tx、rx、rtt等运行状态指标。除原生支持的观测实体,gala-gopher也可以扩展观测实体。 数据表(table_name):观测实体由1张或更多数据表组合而成,通常1张数据表由1个采集任务完成,由此可知单个观测实体可以由多个采集任务共同完成。 meta文件:通过文件定义观测实体(包括内部的数据表),系统内meta文件必须保证唯一,定义不可冲突。规范参考这里。 支持的技术 # 采集范围:参考这里。覆盖网络、I/O、内存、网卡、调度、Redis、kafka、Nginx等内核及基础软件的RED(Request、Error、Delay)数据观测。 系统异常范围:参考这里。覆盖包括TCP、Socket、进程/线程、I/O、调度等超过60个系统隐患点自动巡检及上报能力。 -安装及使用 # 参考这里 -扩展数据采集范围 # 用户如果希望扩展数据采集范围,只需执行2步:定义观测实体,集成数据探针。 -定义观测实体 通过定义观测实体(或者更新原观测实体)用于承载新增采集metrics数据。用户通过meta文件(参考这里)定义观测实体的key、label(可选)、metrics,定义完成后,将meta文件归档在探针目录。 -集成数据探针 用户可以通过各种编程语言(shell、python、java等)包装数据采集软件,并在脚本中按照meta文件定义格式将采集到的数据通过linux管道符形式输出,参考这里。 -参考cAdvisor第三方探针集成案例。 -gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功能,它将定期获取从 gala-gopher 采集的所有观测对象实例的数据,并计算它们之间的拓扑关系,最终将生成的拓扑图保存到图数据库 arangodb 中。 原理及术语 # 参考这里。 -支持的技术 # 支持的拓扑关系类型 +安装及使用 # 参考这里 @@ -39,10 +37,10 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功 Mon, 01 Jan 0001 00:00:00 +0000 https://openeuler.gitee.io/gala-docs/docs/gala-docs/gala_event_api/ - 异常检测&amp;根因定位数据ArangoDB接口使用指导 # 异常检测、根因定位结果数据默认输出到kafka中,由其它子系统订阅消费使用。本文档提供另外一种数据对接方式,指导将kafka中的数据实时同步到ArangoDB中,其它子系统可以直接使用ArangoDB的接口获取异常检测、根因定位的数据。 + 异常检测&amp;根因定位数据ArangoDB接口使用指导 # 异常检测、根因定位结果数据默认输出到kafka中,由其它子系统订阅消费使用。本文档提供另外一种数据对接方式,指导将kafka中的数据实时同步到ArangoDB中,其它子系统可以直接使用ArangoDB的接口获取异常检测、根因定位的数据。 本数据对接方案的原理,通过开源数据采集工具logstash将kafka中的数据实时消费并转存到ArangoDB中。 -1. ArangoDB环境准备 # ArangoDB的安装部署参考官网,详细过程略。下面介绍创建异常检测&amp;根因定位对应的db和collection。 -1.1 db创建 # shell&gt; curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_api/database &lt;&lt;EOF { &quot;name&quot; : &quot;gala_event&quot;, &quot;options&quot; : { &quot;sharding&quot; : &quot;flexible&quot;, &quot;replicationFactor&quot; : 3 } } EOF HTTP/1.1 201 Created content-type: application/json connection: Keep-Alive content-length: 40 server: ArangoDB x-arango-queue-time-seconds: 0.000000 x-content-type-options: nosniff { &quot;error&quot; : false, &quot;code&quot; : 201, &quot;result&quot; : true } 1.2 collection创建 # shell&gt; curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/collection &lt;&lt;EOF { &quot;name&quot; : &quot;gala_event_anteater&quot; } EOF HTTP/1. +1. ArangoDB环境准备 # ArangoDB的安装部署参考官网,详细过程略。下面介绍创建异常检测&amp;根因定位对应的db和collection。 +1.1 db创建 # shell&gt; curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_api/database &lt;&lt;EOF { &quot;name&quot; : &quot;gala_event&quot;, &quot;options&quot; : { &quot;sharding&quot; : &quot;flexible&quot;, &quot;replicationFactor&quot; : 3 } } EOF HTTP/1.1 201 Created content-type: application/json connection: Keep-Alive content-length: 40 server: ArangoDB x-arango-queue-time-seconds: 0.000000 x-content-type-options: nosniff { &quot;error&quot; : false, &quot;code&quot; : 201, &quot;result&quot; : true } 1.2 collection创建 # shell&gt; curl -X POST --header 'accept: application/json' --data-binary @- --dump - http://ip:8529/_db/gala_event/_api/collection &lt;&lt;EOF { &quot;name&quot; : &quot;gala_event_anteater&quot; } EOF HTTP/1. @@ -51,7 +49,7 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功 Mon, 01 Jan 0001 00:00:00 +0000 https://openeuler.gitee.io/gala-docs/docs/gala-docs/gopher_tech/ - TCP(entity_name:tcp_link) # metrics_name table_name metrics_type unit KPI metrics description tgid key 进程ID role key 客户端/服务端 client_ip key 客户端:本地IP;服务端:对端IP server_ip key 客户端:对端IP;服务端:本地IP client_port key 客户端:本地端口;服务端:对端端口 server_port key 客户端:对端端口;服务端:本地端口 protocol key 协议族(IPv4、IPv6) rx_bytes tcp_tx_rx(0x8) Gauge bytes Y rx bytes tx_bytes tcp_tx_rx(0x8) Gauge bytes Y tx bytes rto tcp_rate(0x20) Gauge Retransmission timeOut(us) ato tcp_rate(0x20) Gauge Estimated value of delayed ACK(us) srtt tcp_rtt(0x4) Gauge us Y Smoothed Round Trip Time(us). snd_ssthresh tcp_rate(0x20) Gauge Slow start threshold for congestion control. + TCP(entity_name:tcp_link) # metrics_name table_name metrics_type unit KPI metrics description tgid key 进程ID role key 客户端/服务端 client_ip key 客户端:本地IP;服务端:对端IP server_ip key 客户端:对端IP;服务端:本地IP client_port key 客户端:本地端口;服务端:对端端口 server_port key 客户端:对端端口;服务端:本地端口 protocol key 协议族(IPv4、IPv6) rx_bytes tcp_tx_rx(0x8) Gauge bytes Y rx bytes tx_bytes tcp_tx_rx(0x8) Gauge bytes Y tx bytes rto tcp_rate(0x20) Gauge Retransmission timeOut(us) ato tcp_rate(0x20) Gauge Estimated value of delayed ACK(us) srtt tcp_rtt(0x4) Gauge us Y Smoothed Round Trip Time(us). snd_ssthresh tcp_rate(0x20) Gauge Slow start threshold for congestion control. @@ -60,12 +58,12 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功 Mon, 01 Jan 0001 00:00:00 +0000 https://openeuler.gitee.io/gala-docs/docs/gala-docs/gopher_tech_abnormal/ - 系统异常事件 # 简介 # 目前,gala-gopher和gala-anteater均提供系统异常检测能力。其中gala-gopher支持用户在启动各个探针的时候,通过阈值(包括上下限)设置异常范围,探针会根据阈值判断某个指标是否异常,如果异常则上报异常事件;而gala-anteater是一款基于AI的操作系统异常检测平台,主要涵盖时序数据预处理、异常点发现、以及异常上报等功能,其能够提供更加准确地系统级异常检测能力。 + 系统异常事件 # 简介 # 目前,gala-gopher和gala-anteater均提供系统异常检测能力。其中gala-gopher支持用户在启动各个探针的时候,通过阈值(包括上下限)设置异常范围,探针会根据阈值判断某个指标是否异常,如果异常则上报异常事件;而gala-anteater是一款基于AI的操作系统异常检测平台,主要涵盖时序数据预处理、异常点发现、以及异常上报等功能,其能够提供更加准确地系统级异常检测能力。 gala-gopher和gala-anteater遵循统一的系统级异常数据格式规范,能够更好地兼容其他应用。 -如何开启异常事件 # gala-gopher如何开启异常事件 +如何开启异常事件 # gala-gopher如何开启异常事件 gopher支持异常事件的探针参考支持的异常事件。 探针启动参数开启异常事件上报 -l WARN 。 设置阈值,比如:设置资源利用率上限为80% -U 80,设置资源利用率下限为5% -L 5 。 注: -gala-anteater无需手动开启,其能够自动进行异常事件检测以及异常事件上报。 gala-gopher异常事件开关、阈值通过探针启动参数传递,探针启动参数参考这里。 gala-gopher支持的异常事件 # 本章以观测实体(entity_name)的粒度来介绍其支持的异常事件。 -SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_nsec(Redis/PG) Process(TID:%d, CIP(%s:%u), SIP(%s:%u)) SLI(%s:%llu) exceed the threshold. P1: process ID P2: client ip and port P3: server ip and port P4 command and SLI [-T &lt;&gt;] WARN 输出格式 # { &quot;Timestamp&quot;: &lt;timestamp&gt;, &quot;event_id&quot;: &quot;&lt;timestamp&gt;_&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, # tgid-应用进程号 fd-应用的socket文件描述符 &quot;Attributes&quot;: { &quot;entity_id&quot;: &quot;&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, &quot;event_id&quot;: &quot;&lt;timestamp&gt;_&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, &quot;event_type&quot;: &quot;sys&quot; # sys-表示异常事件类型为系统级 }, &quot;Resource&quot;: { &quot;metrics&quot;: &quot;gala_gopher_sli_&lt;event_name&gt;&quot; # event_name-异常事件名,参考上表第一列 }, &quot;SeverityText&quot;: &quot;WARN&quot;, &quot;SeverityNumber&quot;: 13, &quot;Body&quot;: &quot;Sat Aug 27 17:41:24 2022 WARN Entity(&lt;entity_id&gt;) Process(TID:&lt;tgid&gt;, CIP(&lt;c_ip&gt;:&lt;c_port&gt;), SIP(&lt;s_ip&gt;:&lt;s_port&gt;)) SLI(&lt;cmd&gt;:&lt;rtt_nsec&gt;) exceed the threshold. +gala-anteater无需手动开启,其能够自动进行异常事件检测以及异常事件上报。 gala-gopher异常事件开关、阈值通过探针启动参数传递,探针启动参数参考这里。 gala-gopher支持的异常事件 # 本章以观测实体(entity_name)的粒度来介绍其支持的异常事件。 +SLI # 异常事件名 事件信息 输出参数 输入参数 异常等级 rtt_nsec(Redis/PG) Process(TID:%d, CIP(%s:%u), SIP(%s:%u)) SLI(%s:%llu) exceed the threshold. P1: process ID P2: client ip and port P3: server ip and port P4 command and SLI [-T &lt;&gt;] WARN 输出格式 # { &quot;Timestamp&quot;: &lt;timestamp&gt;, &quot;event_id&quot;: &quot;&lt;timestamp&gt;_&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, # tgid-应用进程号 fd-应用的socket文件描述符 &quot;Attributes&quot;: { &quot;entity_id&quot;: &quot;&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, &quot;event_id&quot;: &quot;&lt;timestamp&gt;_&lt;machine_id&gt;_&lt;entity_name&gt;_&lt;tgid&gt;_&lt;fd&gt;&quot;, &quot;event_type&quot;: &quot;sys&quot; # sys-表示异常事件类型为系统级 }, &quot;Resource&quot;: { &quot;metrics&quot;: &quot;gala_gopher_sli_&lt;event_name&gt;&quot; # event_name-异常事件名,参考上表第一列 }, &quot;SeverityText&quot;: &quot;WARN&quot;, &quot;SeverityNumber&quot;: 13, &quot;Body&quot;: &quot;Sat Aug 27 17:41:24 2022 WARN Entity(&lt;entity_id&gt;) Process(TID:&lt;tgid&gt;, CIP(&lt;c_ip&gt;:&lt;c_port&gt;), SIP(&lt;s_ip&gt;:&lt;s_port&gt;)) SLI(&lt;cmd&gt;:&lt;rtt_nsec&gt;) exceed the threshold. diff --git a/theme/public/docs/gala-docs/introducation/index.html b/theme/public/docs/gala-docs/introducation/index.html index 763b63af08ec2a2c7dfa18613c38031aa8bb876e..ed3c55e9bdf609da1afa1105fdfaceb607f1aec6 100644 --- a/theme/public/docs/gala-docs/introducation/index.html +++ b/theme/public/docs/gala-docs/introducation/index.html @@ -1,466 +1,462 @@ - - - - - - - - - - - - - - - - - - introducation - Gala - + + + + + + + + + + + + + + + + introducation - Gala + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - - - - -
    -
    - -
    - - -
    -
    - -
    -
    - -
    -
    @@ -517,14 +520,14 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
  • 接口介绍
-
  • 系统I/O全栈诊断 +
  • 系统I/O全栈观测
  • -
  • 系统隐患诊断 +
  • 精细化性能Profiling
    • 特性背景
    • 解决方案
    • @@ -532,22 +535,13 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
  • - - - - - - - - + + + + - - - - - -
    - -

    introducation

    -

    - +
    + +

    introducation

    +

    + + + + + + - - - - - -

    背景

    -

    ​ 云场景中基础软件/业务应用之间的边界逐渐上移,基础软件逐渐成为云场景最重要的组成部分,而操作系统又最重要的基础软件之一。 -​ 从业界公开的数据看,云场景的一些重要故障均是与基础软件密切相关。公开数据显示现有主流云厂商月平均故障150+次数,75%的故障<1H,90%<1.5H,少量故障>5H。

    -

    云场景的基础设施、业务场景的复杂性,导致这些故障现象大量集中基础软件(尤其是操作系统)层面,为此openEuler社区规划&孵化A-Ops项目,该项目包括基础设施监控、应用性能监控、应用安全、自动化及监控四大块功能。

    -

    -

    介绍

    -

    ​ 针对云场景的故障特点,根据故障发展阶段划分成:系统隐患、灰度故障、故障 三个阶段,A-Ops规划应用性能监控解决方案,该解决方案包括多个关键组件,本文用于介绍相关gala-ops系列组件。

    -

    ​ gala-ops系列组件定位:云基础设施场景中,针对基础设施灰度故障导致的应用性能劣化、卡顿系统级故障在线诊断。提供包括应用性能诊断、系统性能瓶颈诊断、系统参数修复、系统实时拓扑等特性。

    -

    原理

    -

    通过eBPF技术实现系统白盒化智能观测,实时在线完成系统架构拓扑化,在此基础完成从基础软硬件至应用现象的根因推导过程,且过程可视化。

    -

    三步骤如下:

    -

    -

    快速安装

    -

    架构

    + +

    介绍

    +

    gala-ops是一款C/S架构、基于AI的操作系统亚健康诊断工具。其基于eBPF + java agent无侵入观测技术,并以AI技术辅助,实现亚健康故障(比如性能抖动、错误率提升、系统卡顿等问题现象)分钟级诊断,简化IT基础设施的运维过程。

    +

    背景

    +

    云基础设施在近几年随着云原生、无服务化等技术的实施,其运维的复杂性变得越来越有挑战性,尤其是亚健康问题特点(间歇性出现、持续时间短、问题种类多、涉及范围广等)给云基础设施故障诊断带来重要挑战。亚健康故障诊断的挑战(包括可观测能力、海量数据管理能力、AI算法的泛化能力等)在Linux场景中变的尤为突出。在openEuler开源操作系统中,现有的运维手段不足以及时发现、定位亚健康问题,存在包括:缺乏在线、持续性监控能力;缺乏应用视角精细化的观测能力;缺乏基于全栈观测数据的自动化、AI分析能力等问题。然而,针对亚健康故障的诊断能力其难点包括:

    +
      +
    • 全栈的无侵入可观测观测能力。
    • +
    • 持续、精细化、低负载的监控能力。
    • +
    • 自适应不同应用场景的异常检测、可视化故障推导能力。
    • +
    +

    项目简介

    +

    gala-ops的整体架构如图所示,其整体上是一个C/S架构。在生产节点gala-gopher是一个Linux后台程序,其负责提供全场景、全栈(包括Metrics、Events、Tracing等)的数据采集,其支持通过OpenTelemetry开放生态接口(支持prometheus exporter、kafka client等)将数据传递给管理节点。管理节点部署gala-spider、gala-anteater组件,分别负责集群拓扑计算、可视化根因推导;

    +

    gala-ops架构上依赖一些开源中间件(包括prometheus、kafka、Elastic等),但亦可对接至客户IT系统现有的中间件。gala-ops架构设计提供被集成能力,可以由行业客户IT运维系统集成。其提供两类被集成方式:

    +
      +
    • 软件生态集成方式:可以只使用gala-gopher可观测能力(OpenTelemetry方式获取数据),亦可以使用全部能力,通过prometheus、Elastic、kafka等中间件获取观测数据、异常检测结果、可视化推导结果。
    • +
    • 工具集成方式:将gala-ops提供的能力以Grafana形式集成至客户IT运维系统内。
    • +
    +

    +

    gala-ops可以给客户提供如下运维能力:

    +
      +
    • 在线应用性能抖动诊断:提供数据库类应用性能在线诊断能力,包括网络类(丢包、重传、时延、TCP零窗等)问题、I/O类(磁盘慢盘、I/O性能下降等)问题,调度类(包括sysCPU冲高、死锁等)问题、内存类(OOM、泄漏等)问题等。
    • +
    • 系统性能瓶颈诊断:提供通用场景的TCP、I/O性能抖动问题诊断能力。
    • +
    • 系统隐患巡检:提供内核协议栈丢包、虚拟化网络丢包、TCP异常、I/O时延异常、系统调用异常、资源泄漏、JVM异常、应用RPC异常(包括8种常见协议的错误率、时延等)硬件故障(UCE、磁盘介质错误等)等秒级巡检能力。
    • +
    • 系统全栈I/O可观测:提供面向分布式存储场景的I/O全栈观测能力,包括GuestOS 进程级、Block层的I/O观测能力,以及虚拟化层存储前端I/O观测能力,分布式存储后端I/O观测能力。
    • +
    • 精细化性能Profiling:提供多维度(包括系统、进程、容器、Pod等多个维度)、高精度(10ms采样周期)的性能(包括CPU性能、内存占用、资源占用、系统调用等类型)火焰图、时间线图,可实时在线持续性采集。
    • +
    • K8S Pod全栈可观测及诊断:提供K8S视角的Pod集群业务流实时拓扑能力,Pod性能观测能力、DNS观测能力、SQL观测能力等。
    • +
    +

    gala-ops涉及的关键技术包括如下:

    +
      +
    • 融合型非侵入观测技术:融合eBPF、Java agent等不同观测技术优点,实现多语言(支持C/C++、Java、Go等主流语言)、全软件栈(包括内核、系统调用、基础库Glibc、运行时jvm、基础中间件Nginx/Haproxy等)的观测能力。
    • +
    • 流程拓扑:基于时序化数据(L4/L7层流量等),实时计算生成时序化拓扑结构,动态展现业务集群拓扑变化。
    • +
    • 可视化根因定位:统计推理模型结合全流程拓扑,实现可视化&分钟级的问题根因诊断。
    • +
    +

    应用场景

    +

    ​ gala-ops在openEuler等Linux环境主要面向场景包括数据库、分布式存储、虚拟化、云原生等场景。助力金融、电信、互联网等行业客户在全栈可观测的基础上实现亚健康故障分钟级诊断。

    +

    项目代码仓

    +

    https://gitee.com/openeuler/gala-gopher

    +

    https://gitee.com/openeuler/gala-spider

    +

    https://gitee.com/openeuler/gala-anteater

    +

    快速安装

    +

    架构

    gala-ops是C/S架构,可以集群方式部署,也可以单机部署。整个架构由gala-gopher、gala-ops两个软件组成,在集群模式下,gala-gopher安装在生产节点内,gala-ops安装在管理面节点内;单机模式两者均安装在生产节点内。

    其中,gala-ops软件内包括gala-spidergala-anteatergala-inference组件。

    -

    gala-gopher

    -

    定位

    +

    gala-gopher

    +

    定位

    • 数据采集器:提供应用粒度low-level的数据采集,包括网络、磁盘I/O、调度、内存、安全等方面的系统指标采集,同时负责应用KPI数据的采集。数据类型包括logging、tracing、metrics。
    • 系统异常检测:提供系统异常检测能力,覆盖网络、磁盘I/O、调度、内存等方面的场景系统异常,用户可以通过阈值设置异常上下限范围。
    • 性能热点分析:提供CPU、内存、IO火焰图。
    -

    原理及术语

    +

    原理及术语

    gala-gopher软件架构参考这里,其是一款基于eBPF技术的低负载探针框架,除了其自身采集的数据外,用户可以自由扩展第三方探针。

    术语

      @@ -853,12 +867,12 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
    • 数据表(table_name):观测实体由1张或更多数据表组合而成,通常1张数据表由1个采集任务完成,由此可知单个观测实体可以由多个采集任务共同完成。
    • meta文件:通过文件定义观测实体(包括内部的数据表),系统内meta文件必须保证唯一,定义不可冲突。规范参考这里
    -

    支持的技术

    +

    支持的技术

    采集范围:参考这里。覆盖网络、I/O、内存、网卡、调度、Redis、kafka、Nginx等内核及基础软件的RED(Request、Error、Delay)数据观测。

    系统异常范围:参考这里。覆盖包括TCP、Socket、进程/线程、I/O、调度等超过60个系统隐患点自动巡检及上报能力。

    -

    安装及使用

    +

    安装及使用

    参考这里

    -

    扩展数据采集范围

    +

    扩展数据采集范围

    用户如果希望扩展数据采集范围,只需执行2步:定义观测实体,集成数据探针。

    • 定义观测实体
    • @@ -870,30 +884,30 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功

      用户可以通过各种编程语言(shell、python、java等)包装数据采集软件,并在脚本中按照meta文件定义格式将采集到的数据通过linux管道符形式输出,参考这里

      参考cAdvisor第三方探针集成案例

      -

      gala-spider

      -

      定位

      +

      gala-spider

      +

      定位

      • 拓扑图构建:提供 OS 级别的拓扑图构建功能,它将定期获取从 gala-gopher 采集的所有观测对象实例的数据,并计算它们之间的拓扑关系,最终将生成的拓扑图保存到图数据库 arangodb 中。
      -

      原理及术语

      +

      原理及术语

      参考这里

      -

      支持的技术

      +

      支持的技术

      支持的拓扑关系类型

      OS 观测实体之间往往存在物理上或逻辑上的关系,比如线程和进程之间具有从属关系,进程和进程之间往往会有连接关系。因此,gala-spider 定义了一些通用的拓扑关系类型,详情参见 gala-spider 设计文档:关系类型定义。定义好了拓扑关系类型后,接下来就可以定义观测实体之间的拓扑关系,进而构建拓扑图。

      支持的实体关系列表

      gala-spider 默认定义了一些观测实体之间的拓扑关系,这些拓扑关系是可配置和可扩展的,详情参见 gala-spider 设计文档:支持的拓扑关系

      -

      安装及使用

      +

      安装及使用

      参考这里

      -

      扩展观测实体及关系

      +

      扩展观测实体及关系

      参考这里

      -

      gala-anteater

      -

      定位

      +

      gala-anteater

      +

      定位

      • 异常检测:针对操作系统,提供分钟级别的异常检测能力,能够及时发现潜在影响客户端时延的系统级异常,辅助运维人员,快速跟踪并解决问题。
      • 异常上报:当发现异常行为,平台能够实时上报至Kafka,运维人员只需订阅Kafka消息队列,即可了解当前系统是否潜在风险。
      -

      原理及术语

      +

      原理及术语

      gala-anteater是一款基于AI的操作系统异常检测平台。主要涵盖时序数据预处理、异常点发现、以及异常上报等功能。基于线下预训练、线上模型的增量学习与模型更新,能够很好地适应于多维多模态数据故障诊断。

      • @@ -904,113 +918,366 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功

      -

      安装及使用

      +

      安装及使用

      参考这里

      -

      gala-inference

      -

      定位

      +

      gala-inference

      +

      定位

      • 根因定位:提供异常 KPI 的根因定位能力,它基于异常检测的结果和拓扑图作为输入,并将根因定位的结果输出到 kafka 中。
      -

      原理及术语

      +

      原理及术语

      参考这里

      -

      支持的技术

      +

      支持的技术

      专家规则

      为了提升根因定位结果的准确性和可解释性,我们对操作系统领域内观测实体之间实际存在的一些因果关系进行了分析,并总结出一些通用的专家规则,用于指导后续的根因定位算法。这些通用专家规则的详细内容参见 gala-inference 设计文档:专家规则

      -

      安装及使用

      +

      安装及使用

      参考这里

      -

      gala-ops系统集成

      +

      gala-ops系统集成

      gala-ops还依赖一些开源软件,包括kafka、arangodb、prometheus等。下图介绍gala-ops系统集成关系,kafka用于传输logs/tracing类数据至ES/logstash/jaeger,prometheus用于存储Metrics数据,Arangodb用于存储实时拓扑数据,grafana用于前端页面展示。

      -

      gala-ops系统安装

      -

      A-Ops提供了集成式部署工具A-Ops-Tools以便用户快速部署gala-ops以及其依赖的开源中间件,部署工具的使用约束说明与所有支持选项详细说明可参照A-Ops-Tools部署工具手册

      +

      gala-ops系统安装

      +

      Gala提供了集成式部署工具Gala-Deploy-Tools以便用户快速部署gala-gopher、gala-ops(gala-spider/gala-inference/gala-anteater)组件、kafka/prometheus/arangodb/es/logstash/pyroscope中间件、grafana前端页面展示相关组件,并同时支持离线/在线部署两种模式。

        -
      • -

        获取部署工具

        +
      • kafka 用于传输 gala 软件数据
      • +
      • prometheus 用于存储 gopher metrics数据
      • +
      • arangodb 用于存储 gala-spider 生成的实时拓扑数据
      • +
      • elasticsearch/logstash 存储 gala 数据支持 grafana 前端展示
      • +
      • pyroscope 存储 gopher 火焰图数据
      • +
      • grafana 展示 gala 前端页面
      • +
      +

      约束限制

        -
      1. 下载部署工具压缩包:wget https://gitee.com/Vchanger/a-ops-tools/repository/archive/master.zip –no-check-certificate
      2. -
      3. 使用unzip解压压缩包后进入对应目录即可使用
      4. +
      5. 当前本工具仅支持如下OS版本:openEuler 20.03 LTS SP1(x86)、openEuler 22.03 LTS、openEuler 22.03 LTS SP1、Kylin V10(x86)
      6. +
      7. 在线部署模式下,本工具运行过程中会从openEuler repo源安装rpm或者从外网下载源码资源,因此内网环境在使用工具前需要提前配置好代理,便于访问外网环境,工具使用结束后建议将代理取消。
      8. +
      9. 在线部署模式下,gala-gopher、gala-ops组件支持rpm包部署以及容器部署两种方式,其中gala-ops组件的rpm包部署方式仅支持openEuler 22.03 LTS SP1 版本。
      10. +
      11. 离线部署模式下,gala-gopher会以rpm包的方式安装部署,gala-ops组件会以容器方式安装部署。
      - +

      环境准备说明

      +

      准备至少两台符合OS版本与架构要求(见约束限制1)的机器(物理机、虚拟机均可)并保证机器间网络可以正常连通(在线部署模式下需要连接外网)。

      +
      • -

        部署中间件:kafka/prometheus/arangodb/elasticsearch/logstash

        -

        执行如下命令安装、配置、启动kafka/prometheus/arangodb/elasticsearch/logstash服务,-K/-P/-A/-E选项支持分开使用单独部署对应组件,其中-P用于配置prometheus服务端抓取消息的来源地址(即部署gala-gopher的生产节点)列表,每个地址之间用英文逗号分隔;elasticsearch/logstash由于存在依赖关系,通过-E选项统一控制、绑定安装。

        -
        # sh deploy.sh middleware -K <kafka服务器监听地址> -P <prometheus抓取源地址1[,prometheus抓取源地址2,prometheus抓取源地址3,...]> -E <elasticsearch服务监听地址> -A
        -
        +

        机器A:生产节点,即需要监控运维的目标节点,上面一般运行着业务进程(如数据库、redis、Java应用),用于部署观测组件gala-gopher。

        +

        注:如果有多台生产节点,则每个节点都需要部署gala-gopher

      • -

        部署gala-ops

        -

        gala-ops组件支持rpm、容器镜像两种部署方式,部署时需要指定kafka、prometheus、arangodb服务器地址,当不指定时,这些中间件的地址默认使用localhost。

        +

        机器B:管理节点,用于部署kafka等中间件以及gala的异常检测、根因定位组件。这些组件的部署相对灵活,可以准备多台管理节点分开部署,只要节点之间网络通即可。

        +

        注:管理节点的机器规格建议至少为8U8G

        +
      • +
      +

      离线部署

      +

      gala组件的运行依赖各个中间件,因此建议按照如下顺序(中间件->gala-gopher/gala-ops->grafana)进行安装部署。

      +

      管理节点:部署中间件

      +

      当前涉及的中间件包括kafka、prometheus、arangodb、elasticsearch/logstash、pyroscope共6个组件,其中elasticsearch和logstash存在依赖关系,需要绑定部署。

      +
        +
      1. 离线安装包下载
      2. +
      +

      离线部署前,需要在可连接外网的机器上下载6个中间件的安装包。本工具提供了离线资源下载脚本辅助脚本一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源的下载,下载内容会存放在当前目录的子目录gala_deploy_middleware下。

      +
      sh download_offline_res.sh middleware [os_arch]
      +
      +

      可选选项:

        -
      • -

        rpm方式(仅支持openEuler 22.03 LTS/openEuler 22.03 LTS SP1)

        -
        # sh deploy.sh ops -K <kafka服务器地址> -P <prometheus服务器地址> -A <arangodb地址>
        +
      • os_arch: 指定下载该架构的安装包。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64
      • +
      +

      注:由于kafka运行依赖java,因此下载kafka安装包时也会同时下载java-1.8.0-openjdk及其依赖包;arangodb组件需要下载容器镜像tar包,因此下载机器上需要安装docker组件。*

      +
        +
      1. 工具一键部署
      2. +
      +

      将gala_deploy_middleware下的所有文件和部署脚本辅助脚本 上传到目标管理节点机器上,执行如下命令安装、配置、启动kafka/prometheus/elasticsearch/logstash/arangodb/pyroscope服务,-K/-P/-E/-A/-p选项支持分开使用单独部署对应组件,-S选项来指定离线安装包所在的目录。

      +
      sh deploy.sh middleware -K <部署节点管理IP> -P <prometheus抓取源地址1[,prometheus抓取源地址2,prometheus抓取源地址3,...]> -E <部署节点管理IP> -A -p -S <中间件安装包所在目录>
       
      - -
    • -

      容器镜像方式:

      -
      # sh deploy.sh ops -K <kafka服务器地址> -P <prometheus服务器地址> -A <arangodb地址> --docker
      +

      选项详细说明

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      选项参数说明是否必配
      -K|–kafka使用该选项用于部署kafka服务器,并配置指定的监听IP地址(一般来说是当前节点的管理IP)。当不使用该选项时,不部署kafka服务需要部署kafka服务时为必
      -P|–prometheus使用该选项用于部署prometheus服务器,并配置指定的抓取消息来源(即部署gala-gopher的生产节点)地址列表,每个地址之间用英文逗号分隔,地址后可以跟随“:端口号”来指定抓取端口,当不指定时,使用默认端口8888;地址前可以加上”主机名-“来标识该地址。
      例如:-P 192.168.0.1,192.168.0.2:18001,vm01-192.168.0.3:18002。当不使用该选项时,不部署prometheus服务
      需要部署prometheus服务器时为必配
      -A|–arangodb使用该选项用于部署并启动arangodb数据库服务,该服务默认监听全IP,因此无需指定监听IP。需要部署arangodb时为必配
      -p|–pyroscope使用该选项用于部署并启动pyroscope服务,该服务默认监听全IP,因此无需指定监听IP。需要部署pyroscope服务端时必配
      -E|–elastic使用该选项用于部署elasticsearch、logstash服务,并指定logstash读取消息的elasticsearch服务器地址(一般来说是当前节点的管理IP)。当不使用该选项时,不部署elaticsearch服务需要部署elasticsearch/logstash为必配
      -S|–srcdir离线部署时使用该选项来指定离线安装包所在的目录离线部署时必配
      +

      生产节点:部署gala-gopher

      +
        +
      1. 对应版本gala-gopher及依赖包下载
      2. +
      +

      离线部署前,需要在可连接外网的机器上下载对应版本的gala-gopher包以及其依赖的rpm包。本工具提供了离线资源下载脚本辅助脚本一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_gopher下。

      +
      sh download_offline_res.sh gopher [os_version]  [os_arch]  [docker]
       
      +

      os_version、os_arch 可选项需同时配置使用:

      +
        +
      • +

        os_version: 指定下载该操作系统版本 gala-gopher 软件包。未配置该项时,使用当前系统版本。支持版本列表:openEuler-22.03-LTS-SP1 openEuler-22.03-LTS openEuler-20.03-LTS-SP1 kylin

      • -
      +
    • +

      os_arch: 指定下载该架构 gala-gopher 软件包。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64

    • -

      部署grafana

      -

      执行如下命令完成部署,grafana会以容器实例方式运行。

      -
      # sh deploy.sh grafana
      +

      docker:指定下载 gala-gopher docker 镜像 tar 和 gala-gopher 配置文件(docker 运行 gala-gopher, 将配置文件映射到宿主机上)。命令示例 sh download_offline_res.sh gopher docker 。下载 tar 包 和 gala-gopher 配置文件存放在 gala_deploy_gopher 目录下,文件名格式为gala-gopher-[os_arch]:[os_tag].tar。下载内容如下:

      +
      gala-gopher-aarch64:22.03-lts-sp1.tar
      +gala-gopher.conf
      +gala-gopher-app.conf
      +stackprobe.conf
       
    +
      +
    1. 工具一键部署
    2. +
    +

    将gala_deploy_gopher目录下的所有文件和部署脚本辅助脚本 上传到目标生产节点机器上,执行如下命令安装、配置、启动gala-gopher服务,-S选项来指定离线安装包所在的目录。

    +
    sh deploy.sh gopher -K <kafka服务器地址> -p <pyroscope服务器地址> -S <离线安装包所在目录>  [--docker]
    +
    +

    选项详细说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    选项参数说明是否必配
    -K|–kafka指定gala-gopher上报采集数据的目标kakfa服务器地址,当不配置该选项时,kafka服务器地址使用localhost
    -p|–pyroscope指定gala-gopher开启火焰图功能后火焰图上传到的pyroscope服务器地址(用于对接前端界面显示),当不配置该选项时,pyroscope服务器地址使用localhost
    -S|–srcdir离线部署时使用该选项来指定gala-gopher以及其依赖包所在的目录离线部署时必配
    –docker指定以 docker 方式部署 gala-gopher
    +

    管理节点:部署gala-ops

    +
      +
    1. gala-ops容器镜像下载
    2. +
    +

    离线部署前,需要在可连接外网的机器上下载gala-ops(gala-anteater/gala-spider/gala-inference)容器镜像tar包。本工具提供了离线资源下载脚本辅助脚本一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_ops下。

    +
    sh download_offline_res.sh ops [os_arch]
    +
    +

    可选选项:

    +
      +
    • os_arch: 指定下载该架构的容器镜像。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64
    • +
    +
      +
    1. 工具一键部署
    2. +
    +

    将gala_deploy_ops目录下的所有文件和部署脚本辅助脚本 上传到目标管理节点机器上,执行如下命令安装、配置、启动gala-ops服务,-S选项来指定容器镜像tar包所在的目录。

    +
    sh deploy.sh ops -K <kafka服务器地址> -P <prometheus服务器地址> -A <arangodb地址> -S <gala-ops容器镜像tar包所在目录>
    +
    +

    选项详细说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    选项参数说明是否必配
    -K|–kafka指定gala-ops读取消息的kakfa服务器地址,当不配置该选项时,kafka服务器地址使用localhost
    -P|–prometheus指定gala-ops读取消息的prometheus服务器地址,当不配置该选项时,prometheus服务器地址使用localhost
    -A|–arangodb指定gala-ops存储关系图数据的的arangodb服务器地址,当不配置该选项时,arangodb服务器地址使用localhost
    -S|–srcdir离线部署时使用该选项来指定gala-ops容器镜像tar包所在的目录离线部署时必配
    +

    管理节点:部署grafana

    +
      +
    1. grafana容器镜像与依赖python库下载
    2. +
    +

    离线部署前,需要在可连接外网的机器上下载grafana容器镜像与arangodb2es.py依赖的python库。本工具提供了离线资源下载脚本辅助脚本一键全量下载,将该脚本上传到机器上后执行如下命令完成相关离线资源下载,下载内容会存放在当前目录的子目录gala_deploy_grafana下:

    +
    sh download_offline_res.sh grafana [os_arch]
    +
    +

    可选选项:

    +
      +
    • os_arch: 指定下载该架构的容器镜像。未配置该项时,使用当前系统架构。支持架构列表:aarch64 x86_64
    • +
    +

    注:arangodb2es.py用于将arangodb中的图关系数据转换上传到elasticsearch服务器,以便支持grafana页面上显示拓扑图。*

    +
      +
    1. 工具一键部署
    2. +
    +

    将gala_deploy_grafana下的所有文件、arangodb2es.py部署脚本辅助脚本 上传到目标管理节点,执行如下命令完成部署,grafana会以容器实例方式运行。

    +
    sh deploy.sh grafana -P <Prometheus服务器地址> -p <pyroscope服务器地址> -E <elasticsearch服务器地址> -S <grafana安装包所在目录>
    +
    +

    选项详细说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    选项参数说明是否必配
    -P|–prometheus指定grafana中的prometheus数据源地址,当不配置该选项时,prometheus数据源使用localhost
    -p|–pyroscope指定grafana中读取火焰图的pyroscope数据源地址,当不配置该选项时,pyroscope数据源使用localhost
    -E|–elastic指定grafana中读取异常检测、拓扑图、根因定位结果的elasticsearch数据源地址。当不使用该选项时,elasticsearch数据源使用localhost
    -S|–srcdir离线部署时使用该选项来指定grafana安装包所在的目录离线部署时必配
    +

    在线部署

    +

    获取部署脚本

    +

    下载单独的部署脚本辅助脚本 无需下载整个工具,可以直接通过如下命令下载到待部署机器上:

    +
    wget https://gitee.com/openeuler/gala-docs/raw/master/deploy/deploy.sh --no-check-certificate
    +wget https://gitee.com/openeuler/gala-docs/raw/master/deploy/comm.sh --no-check-certificate
    +
    +

    管理节点:部署中间件

    +

    执行如下命令安装、配置、启动kafka/prometheus/elasticsearch/logstash/arangodb/pyroscope服务,-K/-P/-E/-A/-p选项支持分开使用单独部署对应组件,其中-P用于配置prometheus服务端抓取消息的来源地址(即部署gala-gopher的生产节点)列表,每个地址之间用英文逗号分隔;elasticsearch/logstash由于存在依赖关系,通过-E选项统一控制、绑定安装。

    +
    sh deploy.sh middleware -K <部署节点管理IP> -P <prometheus抓取源地址1[,prometheus抓取源地址2,prometheus抓取源地址3,...]> -E <部署节点管理IP> -A -p
    +
    +
    +

    生产节点:部署gala-gopher

    +

    通过如下命令来安装、配置、启动gala-gopher服务:

    +
      +
    1. rpm方式
    2. +
    +
    sh deploy.sh gopher -K <kafka服务器地址> -p <pyroscope服务器地址>
    +
    +
      +
    1. 容器镜像方式:
    2. +
    +
    sh deploy.sh gopher -K <kafka服务器地址>  -p <pyroscope服务器地址> --docker 
    +
    +

    管理节点:部署gala-ops

    +

    gala-ops组件支持rpm、容器镜像两种部署方式,部署时需要指定kafka、prometheus、arangodb服务器地址,当不指定时,这些中间件的地址默认使用localhost。

    +
      +
    1. rpm方式(仅支持openEuler 22.03 LTS SP1)
    2. +
    +
    sh deploy.sh ops -K <kafka服务器地址> -P <prometheus服务器地址> -A <arangodb地址>
    +
    +
      +
    1. 容器镜像方式:
    2. +
    +
    sh deploy.sh ops -K <kafka服务器地址> -P <prometheus服务器地址> -A <arangodb地址> --docker
    +
    +

    管理节点:部署grafana

    +

    arangodb2es.py下载上传到目标管理节点上,并和部署脚本放在同一个目录,执行如下命令完成前端页面部署,grafana会以容器实例方式运行。

    +
    sh deploy.sh grafana -P <Prometheus服务器地址> -E <es服务器地址>
    +

    gala-ops部署演示视频中以openEuler 22.03 LTS版本为例演示了使用部署工具完成在生成节点上的gala-gopher以及在管理节点上的gala-ops组件部署的过程。 -

    - - - -
    +
    + + + +

    完成上述部署动作后,即可通过浏览器访问“http://[部署节点IP]:3000” 登录grafana来使用A-Ops,登录用户名、密码默认均为admin。A-Ops 总体介绍视频中结合grafana前端展示页面对A-Ops整体功能进行了演示。 -

    - - - -
    +
    + + + +

    -

    项目路线图

    +

    项目路线图

    A-Ops主要选择了8个主力场景,阶段性的落地相关解决方案。gala-ops遵从其场景规划路线图,定义自身特性落地计划,相关场景路线图及落地特性参考下图:

    -

    特性介绍

    -

    在线应用性能诊断

    -

    特性背景

    +

    特性介绍

    +

    在线应用性能诊断

    +

    特性背景

    在云环境中,应用性能受负载、资源等环境因素影响最大,这类因素无法在实验室中模拟,所以在线定位能力显得尤其重要,应用性能诊断存在两个难点:1)无法识别应用性能劣化;2)无法确定问题根因

    • @@ -1049,8 +1316,8 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
    -

    解决方案

    -

    高保真采集应用性能SLI

    +

    解决方案

    +

    高保真采集应用性能SLI

    Google针对云服务SLI的评估提出VALET方法,从5个维度综合评估应用性能。我们借鉴其思路,从吞吐量(容量)、时延2个角度评估应用性能(其他维度后续也可能会纳入评估范围)。

    为了提升通用性(避免语言强相关性、避免应用适配SDK修改等),gala-gopher提供一种相对通用的应用性能SLI采集方法,我们采取从OS内核TCP视角采集应用性能数据(即理论上该方法适用所有基于TCP的应用)。

    @@ -1074,125 +1341,125 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功

    备注:openEuler 22.03 LTS SP1版本gala-gopher采集的应用吞吐量依然来自于应用自身而非OS系统层面。

    -

    基础软件low-level分析

    +

    基础软件low-level分析

    根据前面介绍问题根因的定位离不开OS系统层面的观测,鉴于现有工具的局限性,gala-gopher定位OS系统后台服务,提供基础软件全方位的观测能力,基于eBPF技术,持续性、低底噪的方式为采集基础软件运行时数据(主要是Metrics类型数据)。所有采集的性能Metrics数据,均会携带应用(即进程/线程)标签,实现以应用视角下钻式观测系统运行状态。

    举例:

    -
        {
    -        table_name: "tcp_abn",   --  tcp 异常统计表名
    -        entity_name: "tcp_link",   -- tcp 对象名
    -        fields:
    -        (
    -            {
    -                description: "id of process",
    -                type: "key",
    -                name: "tgid",   --> tcp所属进程号
    -            },
    -            {
    -                description: "role",
    -                type: "key",
    -                name: "role",   --> tcp类型(客户端/服务端)
    -            },
    -            {
    -                description: "client ip",
    -                type: "key",
    -                name: "client_ip",  --> tcp client IP
    -            },
    -            {
    -                description: "server ip",
    -                type: "key",
    -                name: "server_ip",  --> tcp server IP
    -            },
    -            {
    -                description: "client port",  --> 以下均是tcp五元组其他标签
    -                type: "key",
    -                name: "client_port",  
    -            },
    -            {
    -                description: "server port",
    -                type: "key",
    -                name: "server_port",
    -            },
    -            {
    -                description: "protocol",
    -                type: "key",
    -                name: "protocol",
    -            },
    -            {
    -                description: "comm",
    -                type: "label",
    -                name: "comm",    --> tcp所属进程名
    -            },
    -            {
    -                description: "retrans packets",
    -                type: "gauge",
    -                name: "retran_packets",  --> 以下均是tcp异常统计Metrics
    -            },
    -            {
    -                description: "drops caused by backlog queue full",
    -                type: "gauge",
    -                name: "backlog_drops",
    -            },
    -            {
    -                description: "sock drop counter",
    -                type: "gauge",
    -                name: "sk_drops",
    -            },
    -            {
    -                description: "tcp lost counter",
    -                type: "gauge",
    -                name: "lost_out",
    -            },
    -            {
    -                description: "tcp sacked out counter",
    -                type: "gauge",
    -                name: "sacked_out",
    -            },
    -            {
    -                description: "drops caused by socket filter",
    -                type: "gauge",
    -                name: "filter_drops",
    -            },
    -            {
    -                description: "counter of tcp link timeout",
    -                type: "gauge",
    -                name: "tmout_count",
    -            },
    -            .....
    -        )
    -    }
    +
        {
    +        table_name: "tcp_abn",   --  tcp 异常统计表名
    +        entity_name: "tcp_link",   -- tcp 对象名
    +        fields:
    +        (
    +            {
    +                description: "id of process",
    +                type: "key",
    +                name: "tgid",   --> tcp所属进程号
    +            },
    +            {
    +                description: "role",
    +                type: "key",
    +                name: "role",   --> tcp类型(客户端/服务端)
    +            },
    +            {
    +                description: "client ip",
    +                type: "key",
    +                name: "client_ip",  --> tcp client IP
    +            },
    +            {
    +                description: "server ip",
    +                type: "key",
    +                name: "server_ip",  --> tcp server IP
    +            },
    +            {
    +                description: "client port",  --> 以下均是tcp五元组其他标签
    +                type: "key",
    +                name: "client_port",  
    +            },
    +            {
    +                description: "server port",
    +                type: "key",
    +                name: "server_port",
    +            },
    +            {
    +                description: "protocol",
    +                type: "key",
    +                name: "protocol",
    +            },
    +            {
    +                description: "comm",
    +                type: "label",
    +                name: "comm",    --> tcp所属进程名
    +            },
    +            {
    +                description: "retrans packets",
    +                type: "gauge",
    +                name: "retran_packets",  --> 以下均是tcp异常统计Metrics
    +            },
    +            {
    +                description: "drops caused by backlog queue full",
    +                type: "gauge",
    +                name: "backlog_drops",
    +            },
    +            {
    +                description: "sock drop counter",
    +                type: "gauge",
    +                name: "sk_drops",
    +            },
    +            {
    +                description: "tcp lost counter",
    +                type: "gauge",
    +                name: "lost_out",
    +            },
    +            {
    +                description: "tcp sacked out counter",
    +                type: "gauge",
    +                name: "sacked_out",
    +            },
    +            {
    +                description: "drops caused by socket filter",
    +                type: "gauge",
    +                name: "filter_drops",
    +            },
    +            {
    +                description: "counter of tcp link timeout",
    +                type: "gauge",
    +                name: "tmout_count",
    +            },
    +            .....
    +        )
    +    }
     

    数据观测范围包括网络、I/O、内存、调度等,具体可以参考这里

    结合应用性能SLI、基础软件low-level数据观测,建立应用性能大模型,以前者为KPI,后者为特征量,通过gala-ops内相关组件完成线上问题分析,找到对应用性能劣化贡献值最大的特征量(即某个基础软件low-level Metrics)

    -

    案例演示

    +

    案例演示

    数据库与DCS类似,经常也会遇到I/O、网络类因素干扰,造成应用性能波动,下面我们使用openGauss作为演示案例。

    应用性能诊断视频

    -
    - - - -
    +
    + + + +
    -

    系统性能诊断

    -

    特性背景

    +

    系统性能诊断

    +

    特性背景

    系统性能诊断主要用于提供给系统维护SRE日常巡检,提供包括网络(TCP)、I/O等性能劣化的诊断能力。适用于随机性问题追溯,比如网络、I/O性能波动、Socket队列溢出、DNS访问失败、系统调用失败、系统调用超时、进程调度超时等等。

    支持的系统性能诊断类型参考这里

    -

    解决方案

    +

    解决方案

    系统性能诊断分两类:

    • @@ -1212,52 +1479,52 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功

      系统性能波动类异常事件:参考这里。具体包括 TCP建链性能波动、TCP传输时延性能波动、系统I/O时延性能波动、进程I/O时延性能波动、磁盘读写时延性能波动。

    -

    案例演示

    +

    案例演示

    系统性能诊断视频 -

    - - - -
    +
    + + + +

    -

    接口介绍

    +

    接口介绍

    系统性能诊断结果也可以通过kafka topic形式对外通知,诊断结果内标识出具体的观测实体,以及异常原因。

    • 样例1:主机对象内block观测实体异常:

      -
      {
      -  "Timestamp": 1586960586000000000,		// 异常事件时间戳
      -  "event_id": "1586xxx_xxxx"			// 异常事件ID
      -  "Attributes": {
      -    "entity_id": "xx",					// 发生异常的观测实体ID(集群内唯一)
      -    "event_id": "1586xxx_xxxx",			// 异常事件ID(同上)
      -    "event_type": "sys",				// 异常事件类型(sys: 系统异常,app:应用异常)
      -    "data": [....],     // optional
      -    "duration": 30,     // optional
      -    "occurred count": 6,// optional
      -  },
      -  "Resource": {
      -    "metrics": "gala_gopher_block_err_code",	// 产生异常的metrics
      -  },
      -  "SeverityText": "WARN",				// 异常级别
      -  "SeverityNumber": 13,					// 异常级别编号
      -  "Body": "20200415T072306-0700 WARN Entity(xx)  IO errors occured. (Block %d:%d, COMM %s, PID %u, op: %s, datalen %u, err_code %d, scsi_err %d, scsi_tmout %d)."								// 异常事件描述
      -}
      +
      {
      +  "Timestamp": 1586960586000000000,		// 异常事件时间戳
      +  "event_id": "1586xxx_xxxx"			// 异常事件ID
      +  "Attributes": {
      +    "entity_id": "xx",					// 发生异常的观测实体ID(集群内唯一)
      +    "event_id": "1586xxx_xxxx",			// 异常事件ID(同上)
      +    "event_type": "sys",				// 异常事件类型(sys: 系统异常,app:应用异常)
      +    "data": [....],     // optional
      +    "duration": 30,     // optional
      +    "occurred count": 6,// optional
      +  },
      +  "Resource": {
      +    "metrics": "gala_gopher_block_err_code",	// 产生异常的metrics
      +  },
      +  "SeverityText": "WARN",				// 异常级别
      +  "SeverityNumber": 13,					// 异常级别编号
      +  "Body": "20200415T072306-0700 WARN Entity(xx)  IO errors occured. (Block %d:%d, COMM %s, PID %u, op: %s, datalen %u, err_code %d, scsi_err %d, scsi_tmout %d)."								// 异常事件描述
      +}
       

      用户通过kafka订阅到异常事件后,可以表格化管理,以时间段形式呈现管理,如下:

      @@ -1282,8 +1549,8 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
    -

    系统I/O全栈诊断

    -

    特性背景

    +

    系统I/O全栈观测

    +

    特性背景

    分布式存储(包括块存储、对象存储等)是CSP厂商提供的重要服务,常见的分布式存储服务包括EVS、OBS,几乎所有CSP厂商都有这些云服务,同时这些云服务也是其他云服务的存储后端提供者。所以分布式存储的运维效率会决定CSP厂商整个系统的运维效率。

    同时,分布式存储的系统构成复杂,软件来源多样性,系统集群化分布式部署,这些都给该场景的运维带来挑战。具体表现在:

      @@ -1291,7 +1558,7 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
    • 缺乏I/O视角集群运行状态的监控平台,集群型I/O故障诊断能力不足。
    • 缺乏历史问题追溯能力,随机性故障诊断能力不足。
    -

    解决方案

    +

    解决方案

    从I/O数据流视角实时绘制分布式存储集群拓扑图,全栈I/O视角完成对分布式存储系统的I/O数据流进行观测。

    备注:

    @@ -1299,33 +1566,33 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
  • 鉴于分布式存储系统软件来源多样性,这里使用ceph作为示例讲解,不同软件解决方案有不同的观测点。但是主要思路基本相同。
  • openEuler 22.03 SP1发布的系统I/O全栈主要是针对ceph场景(未采用SPDK加速),其分布式存储场景会在后续update版本持续更新。
  • -

    案例演示

    +

    案例演示

    分布式存储I/O全栈诊断视频 -

    - - - -
    +
    + + + +

    -

    系统隐患诊断

    -

    特性背景

    -

    用户在日常运维过程还经常会遇到僵尸进程、内存泄漏、CPU冲高等问题,这些问题现象表现在系统层面,但是问题根因常见在应用层面。为了能够让系统运维SRE快速定界问题范围,gala-ops提供系统隐患诊断能力(隐患是指应用对系统产生的不利影响),用于监控/诊断非健康应用对系统产生的持续性、随机性的隐患,包括CPU冲高、内存泄漏(或持续增长)、I/O带宽拥塞等。

    -

    解决方案

    +

    精细化性能Profiling

    +

    特性背景

    +

    用户在日常运维过程还经常会遇到僵尸进程、内存泄漏、CPU冲高等问题,这些问题现象表现在系统层面,但是问题根因常见在应用层面。为了能够让系统运维SRE快速定界问题范围,gala-ops提供精细化性能Profiling能力,其支持长期、在线采集系统/应用性能数据,可以快速诊断包括CPU冲高、内存泄漏(或持续增长)、系统调用异常、资源不足等问题。

    +

    解决方案

    通过eBPF + 系统perf事件高频采样系统堆栈数据,高度还原故障现场系统运行状态;也可以根据系统资源操作点Hook采样系统堆栈数据,实时还原系统资源使用情况。

    覆盖大部分编程语言(包括C/C++、GO、Rust、java等),提供在线、持续的全栈堆栈信息采集能力。

    @@ -1333,87 +1600,36 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
  • 采样负载评估:以10ms采样一次数据为例,一次采样逻辑的指令预估 1W条( CPU 10MS 指令数量大概能够执行 1KW条指令),采样指令数量/CPU单位时间执行数量 = 1W/1KW 0.1%,所以采样负载理论上是 0.1%(每核)
  • 数据存储评估:采样数据需保存一段时间用于周期性的转换成函数符号。假设采样频率10ms,转换周期1min,那么最少要保留的采样点:1min/10ms * 单次采样数据。即单核大约 6000个采样点。放大评估,单核大约1.2W个采样点。
  • -

    案例演示

    -

    系统隐患诊断视频 -

    - - - -
    +

    gala-ops支持使用Grafana图形界面用来帮助客户更好的理解性能Profiling结果,包括火焰图、时间线图两种形式。

    +

    案例演示

    +

    精细化性能Profiling视频 +

    + + + +

    -

    未来规划

    -

    背景介绍

    -

    云原生是云场景的发展趋势,越来越多的场景采取云原生方式部署业务。在云原生场景的运维软件目前也非常丰富,但是系统层面的运维在云原生场景依然存在一些问题。具体表现在:

    -
      -
    • 在云原生领域,现有成熟监控工具:cAdvisor、Atop、Ganglia等只能看到kernel暴露的数据,无法高保真的监控应用运行状态。
    • -
    • 传统监控APM在面临云原生基础设施厚重的背景下,存在无法深入基础软件内部、无法弹性/动态插桩、语言强依赖等问题。
    • -
    • 基础软件相关的观测工具也存在架构开放性不足(强依赖某种技术,比如istio),引入系统底噪(skywalk引起JVM savepoint)等问题。
    • -
    • 云原生应用的运行状态更多停留在内核中,观测离不开对kernel内运行状态洞察,虽然kernel有cgroup、namespace等抽象,但与云原生应用视角依然存在GAP。
    • -
    -

    总结:现有云原生观测技术存在语言依赖性、底噪高、弹性能力不足、全栈观测能力不足等问题。

    -

    -

    问题及解决思路

    -

    以云原生常见的java应用为例,常见的java应用性能问题通常要经过四个步骤完成定位。过程(归纳示例)参考如下:

    -

    -

    详细步骤介绍如下:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    步骤过程分析存在问题问题总结解决方案
    1通过APM进行集群内分布式跟踪,实现业务实例级定界(定位到某个容器实例)1. skywalking等传统apm存在底噪问题(影响应用吞吐量约10%);
    2. 语言强相关性。
    底噪大,侵入式修改。无侵入分布式Tracing
    2通过perf + AsyncProfier等工具实现容器实例内性能热点抓取,定位至某业务流程。1. linux 、jvm性能数据分开采集,无法统筹分析;
    2. perf等工具无法细粒度采集单个容器实例性能热点数据;
    缺乏全栈细粒度性能数据采集能力全栈细粒度性能火焰图:低底噪、实时全栈(覆盖Linux、JVM) OnCPU、OffCPU、内存热点火焰图
    3通过业务专家分析业务流程,辅助日志、插桩等方式定位至具体函数。1. 日志、插桩等方式存在效率低的问题(需要重新出版本);
    2. 业务流程中的系统性能事件无法观测到(比如线程切换,锁操作,文件操作、网络时延等);
    缺乏业务Request级性能Profling能力Request级性能Profiling:提供在线的Request级性能事件观测能力(包括文件操作、网络访问、锁操作等)
    4如果问题是出现在底层(比如慢I/O),则依赖业务/系统专家会诊,辅助各类工具。1. 依赖人力会诊,效率低;
    2. 随机性故障无法追溯;
    缺乏下钻式全栈观测能力细粒度下钻式全栈观测能力:提供全栈的应用(进程/线程)粒度系统性能数据,并提供应用/系统性能瓶颈分析能力。
    -

    备注:2/3解决方案是gala-ops在云原生场景未来规划的特性,4属于现有特性针对云原生场景的补充增强。

    -

    常见问题

    +

    待发布特性

    +
      +
    1. 系统隐患巡检
    2. +
    3. K8S Pod全栈可观测及诊断
    4. +
    +

    常见问题

    1. 生产环境采集的数据无法送至管理面?
    2. 如何新增数据采集范围?
    3. @@ -1423,52 +1639,53 @@ gala-spider # 定位 # 拓扑图构建:提供 OS 级别的拓扑图构建功
    4. 支持的软件版本范围
    5. 全栈热点分析调用栈为什么不能准确显示函数名?
    -

    用户案例

    -

    合作厂商

    - - - - - -
    - - - - - - - - - - - - - - - - +

    用户案例

    +

    合作厂商

    +

    + + + + + +
    + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/theme/public/docs/index.html b/theme/public/docs/index.html index 393836e6ed73c7d8c76318cd9ce170a2adca87c9..f487b662709eddc814ca44241cf56d2cd0c9e9c8 100644 --- a/theme/public/docs/index.html +++ b/theme/public/docs/index.html @@ -10,7 +10,7 @@ - + @@ -377,10 +377,10 @@ - + - + diff --git a/theme/public/index.html b/theme/public/index.html index 38d76ad580131cd1509c992064f8a530e6366471..da5fc1e4bb3a5a7c2ba9a564c05eeb49f0f6827f 100644 --- a/theme/public/index.html +++ b/theme/public/index.html @@ -10,7 +10,7 @@ - + @@ -239,10 +239,10 @@ - + - + diff --git a/theme/public/index.min.121186177c5ec2a7ecedb7bef90fea681acd16a1830d248417b28c995e349db62feab138d918216e0235b4d0343ce62cab6ae86a625b8dfee4321d665f799fd4.js b/theme/public/index.min.121186177c5ec2a7ecedb7bef90fea681acd16a1830d248417b28c995e349db62feab138d918216e0235b4d0343ce62cab6ae86a625b8dfee4321d665f799fd4.js new file mode 100644 index 0000000000000000000000000000000000000000..9b26d9e1d2564f59070e4b95990f66ececc84686 --- /dev/null +++ b/theme/public/index.min.121186177c5ec2a7ecedb7bef90fea681acd16a1830d248417b28c995e349db62feab138d918216e0235b4d0343ce62cab6ae86a625b8dfee4321d665f799fd4.js @@ -0,0 +1,214 @@ +var suggestions=document.getElementById("suggestions"),search=document.getElementById("search");search!==null&&document.addEventListener("keydown",inputFocus);function inputFocus(e){e.ctrlKey&&e.key==="/"&&(e.preventDefault(),search.focus()),e.key==="Escape"&&(search.blur(),suggestions.classList.add("d-none"))}document.addEventListener("click",function(e){var t=suggestions.contains(e.target);t||suggestions.classList.add("d-none")}),document.addEventListener("keydown",suggestionFocus);function suggestionFocus(e){const s=suggestions.classList.contains("d-none");if(s)return;const t=[...suggestions.querySelectorAll("a")];if(t.length===0)return;const n=t.indexOf(document.activeElement);if(e.key==="ArrowUp"){e.preventDefault();const s=n>0?n-1:0;t[s].focus()}else if(e.key==="ArrowDown"){e.preventDefault();const s=n+1e.result)){if(t.has(e.doc.href))continue;t.set(e.doc.href,e.doc)}if(suggestions.innerHTML="",suggestions.classList.remove("d-none"),t.size===0&&n){const e=document.createElement("div");e.innerHTML=`No results for "${n}"`,e.classList.add("suggestion__no-results"),suggestions.appendChild(e);return}for(const[r,a]of t){const n=document.createElement("div");suggestions.appendChild(n);const e=document.createElement("a");e.href=r,n.appendChild(e);const o=document.createElement("span");o.textContent=a.title,o.classList.add("suggestion__title"),e.appendChild(o);const i=document.createElement("span");if(i.textContent=a.description,i.classList.add("suggestion__description"),e.appendChild(i),suggestions.appendChild(n),suggestions.childElementCount==s)break}}})() \ No newline at end of file diff --git a/theme/public/index.min.48610f9779eeb9fd5eb9fd82277b48cc80133ce7929cd3852bfe719a35d123a8e93f0829a0b82e579ae5352efa10a99041f6365de9c45b9c0c70e6ed2433c9b1.js b/theme/public/index.min.48610f9779eeb9fd5eb9fd82277b48cc80133ce7929cd3852bfe719a35d123a8e93f0829a0b82e579ae5352efa10a99041f6365de9c45b9c0c70e6ed2433c9b1.js new file mode 100644 index 0000000000000000000000000000000000000000..9c759d813675ced1eaca57a9ec626e3e31bb9186 --- /dev/null +++ b/theme/public/index.min.48610f9779eeb9fd5eb9fd82277b48cc80133ce7929cd3852bfe719a35d123a8e93f0829a0b82e579ae5352efa10a99041f6365de9c45b9c0c70e6ed2433c9b1.js @@ -0,0 +1,191 @@ +var suggestions=document.getElementById("suggestions"),search=document.getElementById("search");search!==null&&document.addEventListener("keydown",inputFocus);function inputFocus(e){e.ctrlKey&&e.key==="/"&&(e.preventDefault(),search.focus()),e.key==="Escape"&&(search.blur(),suggestions.classList.add("d-none"))}document.addEventListener("click",function(e){var t=suggestions.contains(e.target);t||suggestions.classList.add("d-none")}),document.addEventListener("keydown",suggestionFocus);function suggestionFocus(e){const s=suggestions.classList.contains("d-none");if(s)return;const t=[...suggestions.querySelectorAll("a")];if(t.length===0)return;const n=t.indexOf(document.activeElement);if(e.key==="ArrowUp"){e.preventDefault();const s=n>0?n-1:0;t[s].focus()}else if(e.key==="ArrowDown"){e.preventDefault();const s=n+1e.result)){if(t.has(e.doc.href))continue;t.set(e.doc.href,e.doc)}if(suggestions.innerHTML="",suggestions.classList.remove("d-none"),t.size===0&&n){const e=document.createElement("div");e.innerHTML=`No results for "${n}"`,e.classList.add("suggestion__no-results"),suggestions.appendChild(e);return}for(const[r,a]of t){const n=document.createElement("div");suggestions.appendChild(n);const e=document.createElement("a");e.href=r,n.appendChild(e);const o=document.createElement("span");o.textContent=a.title,o.classList.add("suggestion__title"),e.appendChild(o);const i=document.createElement("span");if(i.textContent=a.description,i.classList.add("suggestion__description"),e.appendChild(i),suggestions.appendChild(n),suggestions.childElementCount==s)break}}})() \ No newline at end of file diff --git a/theme/public/js/bootstrap.min.f06fe6dc0efc9ee1e806021a0c338278256ecf163b547e8e8769fe60b0a1c0d505782852616ca4e1ff6a719eb8fc954e1d26031b8ab3ca71fb9d9998081b319e.js b/theme/public/js/bootstrap.min.f06fe6dc0efc9ee1e806021a0c338278256ecf163b547e8e8769fe60b0a1c0d505782852616ca4e1ff6a719eb8fc954e1d26031b8ab3ca71fb9d9998081b319e.js new file mode 100644 index 0000000000000000000000000000000000000000..eb87cb9b7e22ee1b3783f6672433942a3ecd6064 --- /dev/null +++ b/theme/public/js/bootstrap.min.f06fe6dc0efc9ee1e806021a0c338278256ecf163b547e8e8769fe60b0a1c0d505782852616ca4e1ff6a719eb8fc954e1d26031b8ab3ca71fb9d9998081b319e.js @@ -0,0 +1 @@ +(()=>{var n=Object.create,e=Object.defineProperty,s=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyNames,o=Object.getPrototypeOf,i=Object.prototype.hasOwnProperty,a=(e,n)=>function(){return n||(0,e[t(e)[0]])((n={exports:{}}).exports,n),n.exports},r=(n,o,a,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let c of t(o))!i.call(n,c)&&c!==a&&e(n,c,{get:()=>o[c],enumerable:!(r=s(o,c))||r.enumerable});return n},c=(t,s,i)=>(i=t!=null?n(o(t)):{},r(s||!t||!t.__esModule?e(i,"default",{value:t,enumerable:!0}):i,t)),l=a({"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"(e,t){!function(n,s){"object"==typeof e&&"undefined"!=typeof t?t.exports=s():"function"==typeof define&&define.amd?define(s):(n="undefined"!=typeof globalThis?globalThis:n||self).bootstrap=s()}(e,function(){"use strict";const y=new Map,_e={set(e,t,n){y.has(e)||y.set(e,new Map);const s=y.get(e);s.has(t)||0===s.size?s.set(t,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,t)=>y.has(e)&&y.get(e).get(t)||null,remove(e,t){if(!y.has(e))return;const n=y.get(e);n.delete(t),0===n.size&&y.delete(e)}},He="transitionend",_t=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,(e,t)=>`#${CSS.escape(t)}`)),e),ft=e=>{e.dispatchEvent(new Event(He))},v=e=>!!e&&"object"==typeof e&&(void 0!==e.jquery&&(e=e[0]),void 0!==e.nodeType),j=e=>v(e)?e.jquery?e[0]:e:"string"==typeof e&&e.length>0?document.querySelector(_t(e)):null,P=e=>{if(!v(e)||0===e.getClientRects().length)return!1;const n="visible"===getComputedStyle(e).getPropertyValue("visibility"),t=e.closest("details:not([open])");if(!t)return n;if(t!==e){const n=e.closest("summary");if(n&&n.parentNode!==t)return!1;if(null===n)return!1}return n},b=e=>!e||e.nodeType!==Node.ELEMENT_NODE||!!e.classList.contains("disabled")||(void 0!==e.disabled?e.disabled:e.hasAttribute("disabled")&&"false"!==e.getAttribute("disabled")),Rt=e=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof e.getRootNode){const t=e.getRootNode();return t instanceof ShadowRoot?t:null}return e instanceof ShadowRoot?e:e.parentNode?Rt(e.parentNode):null},he=()=>{},$=e=>{e.offsetHeight},$t=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Te=[],d=()=>"rtl"===document.documentElement.dir,l=e=>{var t=()=>{const t=$t();if(t){const n=e.NAME,s=t.fn[n];t.fn[n]=e.jQueryInterface,t.fn[n].Constructor=e,t.fn[n].noConflict=()=>(t.fn[n]=s,e.jQueryInterface)}};"loading"===document.readyState?(Te.length||document.addEventListener("DOMContentLoaded",()=>{for(const e of Te)e()}),Te.push(t)):t()},o=(e,t=[],n=e)=>"function"==typeof e?e(...t):n,nn=(e,t,n=!0)=>{if(!n)return void o(e);const a=(e=>{if(!e)return 0;let{transitionDuration:t,transitionDelay:n}=window.getComputedStyle(e);const s=Number.parseFloat(t),o=Number.parseFloat(n);return s||o?(t=t.split(",")[0],n=n.split(",")[0],1e3*(Number.parseFloat(t)+Number.parseFloat(n))):0})(t)+5;let s=!1;const i=({target:n})=>{n===t&&(s=!0,t.removeEventListener(He,i),o(e))};t.addEventListener(He,i),setTimeout(()=>{s||ft(t)},a)},Me=(e,t,n,s)=>{const i=e.length;let o=e.indexOf(t);return-1===o?!n&&s?e[i-1]:e[0]:(o+=n?1:-1,s&&(o=(o+i)%i),e[Math.max(0,Math.min(o,i-1))])},es=/[^.]*(?=\..*)\.|.*/,$n=/\..*/,Dn=/::\d+$/,Ae={};let dn=1;const en={mouseenter:"mouseover",mouseleave:"mouseout"},zn=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function Qt(e,t){return t&&`${t}::${dn++}`||e.uidEvent||dn++}function Bt(e){const t=Qt(e);return e.uidEvent=t,Ae[t]=Ae[t]||{},Ae[t]}function At(e,t,n=null){return Object.values(e).find(e=>e.callable===t&&e.delegationSelector===n)}function xt(e,t,n){const o="string"==typeof t,i=o?n:t||n;let s=dt(e);return zn.has(s)||(s=e),[o,i,s]}function vt(t,n,s,o,i){if("string"!=typeof n||!t)return;let[c,a,l]=xt(n,s,o);if(n in en){const e=e=>function(t){if(!t.relatedTarget||t.relatedTarget!==t.delegateTarget&&!t.delegateTarget.contains(t.relatedTarget))return e.call(this,t)};a=e(a)}const u=Bt(t),h=u[l]||(u[l]={}),d=At(h,a,c?s:null);if(d)return void(d.oneOff=d.oneOff&&i);const m=Qt(a,n.replace(es,"")),r=c?function(t,n,s){return function o(i){const a=t.querySelectorAll(n);for(let{target:r}=i;r&&r!==this;r=r.parentNode)for(const c of a)if(c===r)return Ze(i,{delegateTarget:r}),o.oneOff&&e.off(t,i.type,n,s),s.apply(r,[i])}}(t,s,a):function(t,n){return function s(o){return Ze(o,{delegateTarget:t}),s.oneOff&&e.off(t,o.type,n),n.apply(t,[o])}}(t,a);r.delegationSelector=c?s:null,r.callable=a,r.oneOff=i,r.uidEvent=m,h[m]=r,t.addEventListener(l,r,c)}function We(e,t,n,s,o){const i=At(t[n],s,o);i&&(e.removeEventListener(n,i,Boolean(o)),delete t[n][i.uidEvent])}function Cs(e,t,n,s){const o=t[n]||{};for(const[a,i]of Object.entries(o))a.includes(s)&&We(e,t,n,i.callable,i.delegationSelector)}function dt(e){return e=e.replace($n,""),en[e]||e}const e={on(e,t,n,s){vt(e,t,n,s,!1)},one(e,t,n,s){vt(e,t,n,s,!0)},off(e,t,n,s){if("string"!=typeof t||!e)return;const[c,a,i]=xt(t,n,s),l=i!==t,o=Bt(e),r=o[i]||{},d=t.startsWith(".");if(void 0===a){if(d)for(const n of Object.keys(o))Cs(e,o,n,t.slice(1));for(const[s,n]of Object.entries(r)){const a=s.replace(Dn,"");l&&!t.includes(a)||We(e,o,i,n.callable,n.delegationSelector)}}else{if(!Object.keys(r).length)return;We(e,o,i,a,c?n:null)}},trigger(e,t,n){if("string"!=typeof t||!e)return null;const i=$t();let s=null,a=!0,r=!0,c=!1;t!==dt(t)&&i&&(s=i.Event(t,n),i(e).trigger(s),a=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());const o=Ze(new Event(t,{bubbles:a,cancelable:!0}),n);return c&&o.preventDefault(),r&&e.dispatchEvent(o),o.defaultPrevented&&s&&s.preventDefault(),o}};function Ze(e,t={}){for(const[n,s]of Object.entries(t))try{e[n]=s}catch{Object.defineProperty(e,n,{configurable:!0,get:()=>s})}return e}function ot(e){if("true"===e)return!0;if("false"===e)return!1;if(e===Number(e).toString())return Number(e);if(""===e||"null"===e)return null;if("string"!=typeof e)return e;try{return JSON.parse(decodeURIComponent(e))}catch{return e}}function et(e){return e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}const g={setDataAttribute(e,t,n){e.setAttribute(`data-bs-${et(t)}`,n)},removeDataAttribute(e,t){e.removeAttribute(`data-bs-${et(t)}`)},getDataAttributes(e){if(!e)return{};const t={},n=Object.keys(e.dataset).filter(e=>e.startsWith("bs")&&!e.startsWith("bsConfig"));for(const o of n){let s=o.replace(/^bs/,"");s=s.charAt(0).toLowerCase()+s.slice(1,s.length),t[s]=ot(e.dataset[o])}return t},getDataAttribute:(e,t)=>ot(e.getAttribute(`data-bs-${et(t)}`))};class G{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){const n=v(t)?g.getDataAttribute(t,"config"):{};return{...this.constructor.Default,..."object"==typeof n?n:{},...v(t)?g.getDataAttributes(t):{},..."object"==typeof e?e:{}}}_typeCheckConfig(e,t=this.constructor.DefaultType){for(const[s,o]of Object.entries(t)){const i=e[s],a=v(i)?"element":null==(n=i)?`${n}`:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(a))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${s}" provided type "${a}" but expected type "${o}".`)}var n}}class u extends G{constructor(e,t){super(),(e=j(e))&&(this._element=e,this._config=this._getConfig(t),_e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){_e.remove(this._element,this.constructor.DATA_KEY),e.off(this._element,this.constructor.EVENT_KEY);for(const e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,n=!0){nn(e,t,n)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return _e.get(j(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,"object"==typeof t?t:null)}static get VERSION(){return"5.3.0"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}}const ye=e=>{let t=e.getAttribute("data-bs-target");if(!t||"#"===t){let n=e.getAttribute("href");if(!n||!n.includes("#")&&!n.startsWith("."))return null;n.includes("#")&&!n.startsWith("#")&&(n=`#${n.split("#")[1]}`),t=n&&"#"!==n?n.trim():null}return _t(t)},t={find:(e,t=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(t,e)),findOne:(e,t=document.documentElement)=>Element.prototype.querySelector.call(t,e),children:(e,t)=>[].concat(...e.children).filter(e=>e.matches(t)),parents(e,t){const s=[];let n=e.parentNode.closest(t);for(;n;)s.push(n),n=n.parentNode.closest(t);return s},prev(e,t){let n=e.previousElementSibling;for(;n;){if(n.matches(t))return[n];n=n.previousElementSibling}return[]},next(e,t){let n=e.nextElementSibling;for(;n;){if(n.matches(t))return[n];n=n.nextElementSibling}return[]},focusableChildren(e){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>`${e}:not([tabindex^="-"])`).join(",");return this.find(t,e).filter(e=>!b(e)&&P(e))},getSelectorFromElement(e){const n=ye(e);return n&&t.findOne(n)?n:null},getElementFromSelector(e){const n=ye(e);return n?t.findOne(n):null},getMultipleElementsFromSelector(e){const n=ye(e);return n?t.find(n):[]}},ue=(n,s="hide")=>{const i=`click.dismiss${n.EVENT_KEY}`,o=n.NAME;e.on(document,i,`[data-bs-dismiss="${o}"]`,function(e){if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),b(this))return;const i=t.getElementFromSelector(this)||this.closest(`.${o}`);n.getOrCreateInstance(i)[s]()})};class re extends u{static get NAME(){return"alert"}close(){if(e.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback(()=>this._destroyElement(),this._element,t)}_destroyElement(){this._element.remove(),e.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(e){return this.each(function(){const t=re.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}ue(re,"close"),l(re);const it='[data-bs-toggle="button"]';class ae extends u{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(e){return this.each(function(){const t=ae.getOrCreateInstance(this);"toggle"===e&&t[e]()})}}e.on(document,"click.bs.button.data-api",it,e=>{e.preventDefault();const t=e.target.closest(it);ae.getOrCreateInstance(t).toggle()}),l(ae);const ks={endCallback:null,leftCallback:null,rightCallback:null},As={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Ke extends G{constructor(e,t){super(),this._element=e,e&&Ke.isSupported()&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return ks}static get DefaultType(){return As}static get NAME(){return"swipe"}dispose(){e.off(this._element,".bs.swipe")}_start(e){this._supportPointerEvents?this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX):this._deltaX=e.touches[0].clientX}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),o(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){const e=Math.abs(this._deltaX);if(e<=40)return;const t=e/this._deltaX;this._deltaX=0,t&&o(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(e.on(this._element,"pointerdown.bs.swipe",e=>this._start(e)),e.on(this._element,"pointerup.bs.swipe",e=>this._end(e)),this._element.classList.add("pointer-event")):(e.on(this._element,"touchstart.bs.swipe",e=>this._start(e)),e.on(this._element,"touchmove.bs.swipe",e=>this._move(e)),e.on(this._element,"touchend.bs.swipe",e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&("pen"===e.pointerType||"touch"===e.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const X="next",S="prev",F="left",J="right",Ue="slid.bs.carousel",pt="carousel",ne="active",Ss={ArrowLeft:J,ArrowRight:F},Ms={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Fs={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class K extends u{constructor(e,n){super(e,n),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=t.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===pt&&this.cycle()}static get Default(){return Ms}static get DefaultType(){return Fs}static get NAME(){return"carousel"}next(){this._slide(X)}nextWhenVisible(){!document.hidden&&P(this._element)&&this.next()}prev(){this._slide(S)}pause(){this._isSliding&&ft(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?e.one(this._element,Ue,()=>this.cycle()):this.cycle())}to(t){const n=this._getItems();if(t>n.length-1||t<0)return;if(this._isSliding)return void e.one(this._element,Ue,()=>this.to(t));const s=this._getItemIndex(this._getActive());if(s===t)return;const o=t>s?X:S;this._slide(o,n[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&e.on(this._element,"keydown.bs.carousel",e=>this._keydown(e)),"hover"===this._config.pause&&(e.on(this._element,"mouseenter.bs.carousel",()=>this.pause()),e.on(this._element,"mouseleave.bs.carousel",()=>this._maybeEnableCycle())),this._config.touch&&Ke.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const n of t.find(".carousel-item img",this._element))e.on(n,"dragstart.bs.carousel",e=>e.preventDefault());const n={leftCallback:()=>this._slide(this._directionToOrder(F)),rightCallback:()=>this._slide(this._directionToOrder(J)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),500+this._config.interval))}};this._swipeHelper=new Ke(this._element,n)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;const t=Ss[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;const s=t.findOne(".active",this._indicatorsElement);s.classList.remove(ne),s.removeAttribute("aria-current");const n=t.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);n&&(n.classList.add(ne),n.setAttribute("aria-current","true"))}_updateInterval(){const e=this._activeElement||this._getActive();if(!e)return;const t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(t,n=null){if(this._isSliding)return;const o=this._getActive(),a=t===X,s=n||Me(this._getItems(),o,a,this._config.wrap);if(s===o)return;const c=this._getItemIndex(s),l=n=>e.trigger(this._element,n,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(o),to:c});if(l("slide.bs.carousel").defaultPrevented)return;if(!o||!s)return;const d=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(c),this._activeElement=s;const i=a?"carousel-item-start":"carousel-item-end",r=a?"carousel-item-next":"carousel-item-prev";s.classList.add(r),$(s),o.classList.add(i),s.classList.add(i),this._queueCallback(()=>{s.classList.remove(i,r),s.classList.add(ne),o.classList.remove(ne,r,i),this._isSliding=!1,l(Ue)},o,this._isAnimated()),d&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return t.findOne(".active.carousel-item",this._element)}_getItems(){return t.find(".carousel-item",this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return d()?e===F?S:X:e===F?X:S}_orderToDirection(e){return d()?e===S?F:J:e===S?J:F}static jQueryInterface(e){return this.each(function(){const t=K.getOrCreateInstance(this,e);if("number"!=typeof e){if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}}else t.to(e)})}}e.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",function(e){const s=t.getElementFromSelector(this);if(!s||!s.classList.contains(pt))return;e.preventDefault();const n=K.getOrCreateInstance(s),o=this.getAttribute("data-bs-slide-to");return o?(n.to(o),void n._maybeEnableCycle()):"next"===g.getDataAttribute(this,"slide")?(n.next(),void n._maybeEnableCycle()):(n.prev(),void n._maybeEnableCycle())}),e.on(window,"load.bs.carousel.data-api",()=>{const e=t.find('[data-bs-ride="carousel"]');for(const t of e)K.getOrCreateInstance(t)}),l(K);const Pe="show",Z="collapse",le="collapsing",xe='[data-bs-toggle="collapse"]',Ds={parent:null,toggle:!0},fs={parent:"(null|element)",toggle:"boolean"};class V extends u{constructor(e,n){super(e,n),this._isTransitioning=!1,this._triggerArray=[];const s=t.find(xe);for(const e of s){const n=t.getSelectorFromElement(e),o=t.find(n).filter(e=>e===this._element);null!==n&&o.length&&this._triggerArray.push(e)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ds}static get DefaultType(){return fs}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let n=[];if(this._config.parent&&(n=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter(e=>e!==this._element).map(e=>V.getOrCreateInstance(e,{toggle:!1}))),n.length&&n[0]._isTransitioning)return;if(e.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const e of n)e.hide();const t=this._getDimension();this._element.classList.remove(Z),this._element.classList.add(le),this._element.style[t]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s=`scroll${t[0].toUpperCase()+t.slice(1)}`;this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(le),this._element.classList.add(Z,Pe),this._element.style[t]="",e.trigger(this._element,"shown.bs.collapse")},this._element,!0),this._element.style[t]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(e.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const n=this._getDimension();this._element.style[n]=`${this._element.getBoundingClientRect()[n]}px`,$(this._element),this._element.classList.add(le),this._element.classList.remove(Z,Pe);for(const e of this._triggerArray){const n=t.getElementFromSelector(e);n&&!this._isShown(n)&&this._addAriaAndCollapsedClass([e],!1)}this._isTransitioning=!0,this._element.style[n]="",this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(le),this._element.classList.add(Z),e.trigger(this._element,"hidden.bs.collapse")},this._element,!0)}_isShown(e=this._element){return e.classList.contains(Pe)}_configAfterMerge(e){return e.toggle=Boolean(e.toggle),e.parent=j(e.parent),e}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const e=this._getFirstLevelChildren(xe);for(const n of e){const s=t.getElementFromSelector(n);s&&this._addAriaAndCollapsedClass([n],this._isShown(s))}}_getFirstLevelChildren(e){const n=t.find(":scope .collapse .collapse",this._config.parent);return t.find(e,this._config.parent).filter(e=>!n.includes(e))}_addAriaAndCollapsedClass(e,t){if(e.length)for(const n of e)n.classList.toggle("collapsed",!t),n.setAttribute("aria-expanded",t)}static jQueryInterface(e){const t={};return"string"==typeof e&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){const n=V.getOrCreateInstance(this,t);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e]()}})}}e.on(document,"click.bs.collapse.data-api",xe,function(e){("A"===e.target.tagName||e.delegateTarget&&"A"===e.delegateTarget.tagName)&&e.preventDefault();for(const e of t.getMultipleElementsFromSelector(this))V.getOrCreateInstance(e,{toggle:!1}).toggle()}),l(V);var q,s="top",a="bottom",i="right",n="left",be="auto",L=[s,a,i,n],O="start",R="end",Fn="clippingParents",Oe="viewport",N="popper",Cn="reference",Ee=L.reduce(function(e,t){return e.concat([t+"-"+O,t+"-"+R])},[]),ke=[].concat(L,[be]).reduce(function(e,t){return e.concat([t,t+"-"+O,t+"-"+R])},[]),On="beforeRead",cn="read",on="afterRead",tn="beforeMain",Zt="main",nt="afterMain",Ht="beforeWrite",Nt="write",Ct="afterWrite",wt=[On,cn,on,tn,Zt,nt,Ht,Nt,Ct],ut,gt,bt,De;function h(e){return e?(e.nodeName||"").toLowerCase():null}function r(e){if(e==null)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function x(e){return e instanceof r(e).Element||e instanceof Element}function c(e){return e instanceof r(e).HTMLElement||e instanceof HTMLElement}function Ve(e){return"undefined"!=typeof ShadowRoot&&(e instanceof r(e).ShadowRoot||e instanceof ShadowRoot)}const $e={name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach(function(e){var o=t.styles[e]||{},s=t.attributes[e]||{},n=t.elements[e];c(n)&&h(n)&&(Object.assign(n.style,o),Object.keys(s).forEach(function(e){var t=s[e];!1===t?n.removeAttribute(e):n.setAttribute(e,!0===t?"":t)}))})},effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach(function(e){var s=t.elements[e],o=t.attributes[e]||{},i=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce(function(e,t){return e[t]="",e},{});c(s)&&h(s)&&(Object.assign(s.style,i),Object.keys(o).forEach(function(e){s.removeAttribute(e)}))})}},requires:["computeStyles"]};function f(e){return e.split("-")[0]}var E=Math.max,Q=Math.min,M=Math.round;function Ye(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function mt(){return!/^((?!chrome|android).)*safari/i.test(Ye())}function k(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=!1);var s=e.getBoundingClientRect(),i=1,a=1;t&&c(e)&&(i=e.offsetWidth>0&&M(s.width)/e.offsetWidth||1,a=e.offsetHeight>0&&M(s.height)/e.offsetHeight||1);var o=(x(e)?r(e):window).visualViewport,u=!mt()&&n,l=(s.left+(u&&o?o.offsetLeft:0))/i,d=(s.top+(u&&o?o.offsetTop:0))/a,h=s.width/i,m=s.height/a;return{width:h,height:m,top:d,right:l+h,bottom:d+m,left:l,x:l,y:d}}function Qe(e){var t=k(e),n=e.offsetWidth,s=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-s)<=1&&(s=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:s}}function at(e,t){var n,s=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(s&&Ve(s)){n=t;do{if(n&&e.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function p(e){return r(e).getComputedStyle(e)}function Es(e){return["table","td","th"].indexOf(h(e))>=0}function w(e){return((x(e)?e.ownerDocument:e.document)||window.document).documentElement}function me(e){return"html"===h(e)?e:e.assignedSlot||e.parentNode||(Ve(e)?e.host:null)||w(e)}function st(e){return c(e)&&"fixed"!==p(e).position?e.offsetParent:null}function I(e){for(var n=r(e),t=st(e);t&&Es(t)&&"static"===p(t).position;)t=st(t);return t&&("html"===h(t)||"body"===h(t)&&"static"===p(t).position)?n:t||function(e){var t,n,s=/firefox/i.test(Ye());if(/Trident/i.test(Ye())&&c(e)&&"fixed"===p(e).position)return null;t=me(e);for(Ve(t)&&(t=t.host);c(t)&&["html","body"].indexOf(h(t))<0;){if(n=p(t),"none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||s&&"filter"===n.willChange||s&&n.filter&&"none"!==n.filter)return t;t=t.parentNode}return null}(e)||n}function Je(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function U(e,t,n){return E(e,Q(t,n))}function rt(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function ct(e,t){return t.reduce(function(t,n){return t[n]=e,t},{})}const lt={name:"arrow",enabled:!0,phase:"main",fn:function(e){var l,t=e.state,j=e.name,k=e.options,u=t.elements.arrow,d=t.modifiersData.popperOffsets,h=f(t.placement),o=Je(h),r=[n,i].indexOf(h)>=0?"height":"width";if(u&&d){var p=function(e,t){return rt("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:ct(e,L))}(k.padding,t),g=Qe(u),E="y"===o?s:n,w="y"===o?a:i,_=t.rects.reference[r]+t.rects.reference[o]-d[o]-t.rects.popper[r],y=d[o]-t.rects.reference[o],c=I(u),b=c?"y"===o?c.clientHeight||0:c.clientWidth||0:0,O=_/2-y/2,x=p[E],C=b-g[r]-p[w],v=b/2-g[r]/2+O,m=U(x,v,C),A=o;t.modifiersData[j]=((l={})[A]=m,l.centerOffset=m-v,l)}},effect:function(e){var n=e.state,s=e.options.element,t=void 0===s?"[data-popper-arrow]":s;t!=null&&("string"!=typeof t||(t=n.elements.popper.querySelector(t)))&&at(n.elements.popper,t)&&(n.elements.arrow=t)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function z(e){return e.split("-")[1]}ut={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ht(e){var h,f,b,y,u=e.popper,z=e.popperRect,l=e.placement,S=e.variation,m=e.offsets,A=e.position,v=e.gpuAcceleration,O=e.adaptive,j=e.roundOffsets,k=e.isFixed,N=m.x,t=void 0===N?0:N,D=m.y,o=void 0===D?0:D,E="function"==typeof j?j({x:t,y:o}):{x:t,y:o},t=E.x,o=E.y,x=m.hasOwnProperty("x"),C=m.hasOwnProperty("y"),g=n,_=s,c=window;if(O){var d=I(u),F="clientHeight",T="clientWidth";d===r(u)&&"static"!==p(d=w(u)).position&&"absolute"===A&&(F="scrollHeight",T="scrollWidth"),(l===s||(l===n||l===i)&&S===R)&&(_=a,o-=(k&&d===c&&c.visualViewport?c.visualViewport.height:d[F])-z.height,o*=v?1:-1),l!==n&&(l!==s&&l!==a||S!==R)||(g=i,t-=(k&&d===c&&c.visualViewport?c.visualViewport.width:d[T])-z.width,t*=v?1:-1)}return y=Object.assign({position:A},O&&ut),b=!0===j?function(e,t){var s=e.x,o=e.y,n=t.devicePixelRatio||1;return{x:M(s*n)/n||0,y:M(o*n)/n||0}}({x:t,y:o},r(u)):{x:t,y:o},t=b.x,o=b.y,v?Object.assign({},y,((h={})[_]=C?"0":"",h[g]=x?"0":"",h.transform=(c.devicePixelRatio||1)<=1?"translate("+t+"px, "+o+"px)":"translate3d("+t+"px, "+o+"px, 0)",h)):Object.assign({},y,((f={})[_]=C?o+"px":"",f[g]=x?t+"px":"",f.transform="",f))}const Ge={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options,s=n.gpuAcceleration,c=void 0===s||s,o=n.adaptive,l=void 0===o||o,i=n.roundOffsets,a=void 0===i||i,r={placement:f(t.placement),variation:z(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:c,isFixed:"fixed"===t.options.strategy};null!=t.modifiersData.popperOffsets&&(t.styles.popper=Object.assign({},t.styles.popper,ht(Object.assign({},r,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:l,roundOffsets:a})))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign({},t.styles.arrow,ht(Object.assign({},r,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:a})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})},data:{}};q={passive:!0};const je={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var n=e.state,t=e.instance,s=e.options,o=s.scroll,i=void 0===o||o,a=s.resize,c=void 0===a||a,l=r(n.elements.popper),d=[].concat(n.scrollParents.reference,n.scrollParents.popper);return i&&d.forEach(function(e){e.addEventListener("scroll",t.update,q)}),c&&l.addEventListener("resize",t.update,q),function(){i&&d.forEach(function(e){e.removeEventListener("scroll",t.update,q)}),c&&l.removeEventListener("resize",t.update,q)}},data:{}};gt={left:"right",right:"left",bottom:"top",top:"bottom"};function ee(e){return e.replace(/left|right|bottom|top/g,function(e){return gt[e]})}bt={start:"end",end:"start"};function jt(e){return e.replace(/start|end/g,function(e){return bt[e]})}function Be(e){var t=r(e);return{scrollLeft:t.pageXOffset,scrollTop:t.pageYOffset}}function Ie(e){return k(w(e)).left+Be(e).scrollLeft}function Re(e){var t=p(e),n=t.overflow,s=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+s)}function Ot(e){return["html","body","#document"].indexOf(h(e))>=0?e.ownerDocument.body:c(e)&&Re(e)?e:Ot(me(e))}function B(e,t){void 0===t&&(t=[]);var s,n=Ot(e),o=n===(null==(s=e.ownerDocument)?void 0:s.body),i=r(n),a=o?[i].concat(i.visualViewport||[],Re(n)?n:[]):n,c=t.concat(a);return o?c:c.concat(B(me(a)))}function Le(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function tt(e,t,n){return t===Oe?Le(function(e,t){var s,d=r(e),o=w(e),n=d.visualViewport,i=o.clientWidth,a=o.clientHeight,c=0,l=0;return n&&(i=n.width,a=n.height,s=mt(),(s||!s&&"fixed"===t)&&(c=n.offsetLeft,l=n.offsetTop)),{width:i,height:a,x:c+Ie(e),y:l}}(e,n)):x(t)?function(e,t){var n=k(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(t,n):Le(function(e){var s,n=w(e),o=Be(e),t=null==(s=e.ownerDocument)?void 0:s.body,i=E(n.scrollWidth,n.clientWidth,t?t.scrollWidth:0,t?t.clientWidth:0),r=E(n.scrollHeight,n.clientHeight,t?t.scrollHeight:0,t?t.clientHeight:0),a=-o.scrollLeft+Ie(e),c=-o.scrollTop;return"rtl"===p(t||n).direction&&(a+=E(n.clientWidth,t?t.clientWidth:0)-i),{width:i,height:r,x:a,y:c}}(w(e)))}function kt(e){var o,r,l,t=e.reference,c=e.element,d=e.placement,u=d?f(d):null,p=d?z(d):null,h=t.x+t.width/2-c.width/2,m=t.y+t.height/2-c.height/2;switch(u){case s:o={x:h,y:t.y-c.height};break;case a:o={x:h,y:t.y+t.height};break;case i:o={x:t.x+t.width,y:m};break;case n:o={x:t.x-c.width,y:m};break;default:o={x:t.x,y:t.y}}if(r=u?Je(u):null,r!=null)switch(l="y"===r?"height":"width",p){case O:o[r]=o[r]-(t[l]/2-c[l]/2);break;case R:o[r]=o[r]+(t[l]/2-c[l]/2)}return o}function A(e,t){void 0===t&&(t={});var A,n=t,y=n.placement,O=void 0===y?e.placement:y,v=n.strategy,V=void 0===v?e.strategy:v,b=n.boundary,T=void 0===b?Fn:b,M=n.rootBoundary,H=void 0===M?Oe:M,F=n.elementContext,l=void 0===F?N:F,g=n.altBoundary,P=void 0!==g&&g,_=n.padding,u=void 0===_?0:_,o=rt("number"!=typeof u?u:ct(u,L)),R=l===N?Cn:N,S=e.rects.popper,f=e.elements[P?R:l],r=function(e,t,n,s){var a="clippingParents"===t?function(e){var n=B(me(e)),t=["absolute","fixed"].indexOf(p(e).position)>=0&&c(e)?I(e):e;return x(t)?n.filter(function(e){return x(e)&&at(e,t)&&"body"!==h(e)}):[]}(e):[].concat(t),i=[].concat(a,[n]),r=i[0],o=i.reduce(function(t,n){var o=tt(e,n,s);return t.top=E(o.top,t.top),t.right=Q(o.right,t.right),t.bottom=Q(o.bottom,t.bottom),t.left=E(o.left,t.left),t},tt(e,r,s));return o.width=o.right-o.left,o.height=o.bottom-o.top,o.x=o.left,o.y=o.top,o}(x(f)?f:f.contextElement||w(e.elements.popper),T,H,V),C=k(e.elements.reference),z=kt({reference:C,element:S,strategy:"absolute",placement:O}),D=Le(Object.assign({},S,z)),d=l===N?D:C,m={top:r.top-d.top+o.top,bottom:d.bottom-r.bottom+o.bottom,left:r.left-d.left+o.left,right:d.right-r.right+o.right},j=e.modifiersData.offset;return l===N&&j&&(A=j[O],Object.keys(m).forEach(function(e){var t=[i,a].indexOf(e)>=0?1:-1,n=[s,a].indexOf(e)>=0?"y":"x";m[e]+=A[n]*t})),m}function xs(e,t){void 0===t&&(t={});var s,n=t,c=n.placement,l=n.boundary,d=n.rootBoundary,u=n.padding,h=n.flipVariations,i=n.allowedAutoPlacements,m=void 0===i?ke:i,a=z(c),r=a?h?Ee:Ee.filter(function(e){return z(e)===a}):L,o=r.filter(function(e){return m.indexOf(e)>=0});return 0===o.length&&(o=r),s=o.reduce(function(t,n){return t[n]=A(e,{placement:n,boundary:l,rootBoundary:d,padding:u})[f(n)],t},{}),Object.keys(s).sort(function(e,t){return s[e]-s[t]})}const Mt={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,o=e.options,y=e.name;if(!t.modifiersData[y]._skip){for(var r,d,_,x,w=o.mainAxis,I=void 0===w||w,E=o.altAxis,L=void 0===E||E,R=o.fallbackPlacements,j=o.padding,b=o.boundary,C=o.rootBoundary,$=o.altBoundary,F=o.flipVariations,p=void 0===F||F,W=o.allowedAutoPlacements,l=t.options.placement,V=f(l),B=R||(V!==l&&p?function(e){if(f(e)===be)return[];var t=ee(e);return[jt(e),t,jt(t)]}(l):[ee(l)]),m=[l].concat(B).reduce(function(e,n){return e.concat(f(n)===be?xs(t,{placement:n,boundary:b,rootBoundary:C,padding:j,flipVariations:p,allowedAutoPlacements:W}):n)},[]),H=t.rects.reference,P=t.rects.popper,S=new Map,M=!0,h=m[0],v=0;v=0,k=T?"width":"height",g=A(t,{placement:c,boundary:b,rootBoundary:C,altBoundary:$,padding:j}),u=T?D?i:n:D?a:s;if(H[k]>P[k]&&(u=ee(u)),x=ee(u),r=[],I&&r.push(g[N]<=0),L&&r.push(g[u]<=0,g[x]<=0),r.every(function(e){return e})){h=c,M=!1;break}S.set(c,r)}if(M)for(_=function(e){var t=m.find(function(t){var n=S.get(t);if(n)return n.slice(0,e).every(function(e){return e})});if(t)return h=t,"break"},d=p?3:1;d>0&&"break"!==_(d);d--);t.placement!==h&&(t.modifiersData[y]._skip=!0,t.placement=h,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function Ft(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Tt(e){return[s,i,a,n].some(function(t){return e[t]>=0})}const zt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,a=e.name,r=t.rects.reference,c=t.rects.popper,l=t.modifiersData.preventOverflow,d=A(t,{elementContext:"reference"}),u=A(t,{altBoundary:!0}),n=Ft(d,r),s=Ft(u,c,l),o=Tt(n),i=Tt(s);t.modifiersData[a]={referenceClippingOffsets:n,popperEscapeOffsets:s,isReferenceHidden:o,hasPopperEscaped:i},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":o,"data-popper-escaped":i})}},Dt={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,c=e.options,l=e.name,o=c.offset,d=void 0===o?[0,0]:o,a=ke.reduce(function(e,o){return e[o]=function(e,t,o){var c=f(e),d=[n,s].indexOf(c)>=0?-1:1,l="function"==typeof o?o(Object.assign({},t,{placement:e})):o,a=l[0],r=l[1];return a=a||0,r=(r||0)*d,[n,i].indexOf(c)>=0?{x:r,y:a}:{x:a,y:r}}(o,t.rects,d),e},{}),r=a[t.placement],u=r.x,h=r.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=u,t.modifiersData.popperOffsets.y+=h),t.modifiersData[l]=a}},Ne={name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state,n=e.name;t.modifiersData[n]=kt({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},Lt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,c=e.options,ve=e.name,Z=c.mainAxis,me=void 0===Z||Z,ie=c.altAxis,ae=void 0!==ie&&ie,be=c.boundary,je=c.rootBoundary,ye=c.altBoundary,_e=c.padding,L=c.tether,u=void 0===L||L,N=c.tetherOffset,k=void 0===N?0:N,_=A(t,{boundary:be,rootBoundary:je,padding:_e,altBoundary:ye}),D=f(t.placement),C=z(t.placement),ne=!C,o=Je(D),g="x"===o?"y":"x",v=t.modifiersData.popperOffsets,d=t.rects.reference,p=t.rects.popper,w="function"==typeof k?k(Object.assign({},t.rects,{placement:t.placement})):k,m="number"==typeof w?{mainAxis:w,altAxis:w}:Object.assign({mainAxis:0,altAxis:0},w),y=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,S={x:0,y:0};if(v){if(me){var P,F="y"===o?s:n,B="y"===o?a:i,r="y"===o?"height":"width",h=v[o],W=h+_[F],K=h-_[B],T=u?-p[r]/2:0,ge=C===O?d[r]:p[r],pe=C===O?-p[r]:-d[r],X=t.elements.arrow,fe=u&&X?Qe(X):{width:0,height:0},J=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},ee=J[F],te=J[B],b=U(0,d[r],fe[r]),de=ne?d[r]/2-T-b-ee-m.mainAxis:ge-b-ee-m.mainAxis,le=ne?-d[r]/2+T+b+te+m.mainAxis:pe+b+te+m.mainAxis,x=t.elements.arrow&&I(t.elements.arrow),re=x?"y"===o?x.clientTop||0:x.clientLeft||0:0,q=null!=(P=y?.[o])?P:0,ce=h+le-q,oe=U(u?Q(W,h+de-q-re):W,h,u?E(K,ce):K);v[o]=oe,S[o]=oe-h}if(ae){var se,ue="x"===o?s:n,he="x"===o?a:i,l=v[g],j="y"===g?"height":"width",G=l+_[ue],Y=l-_[he],M=-1!==[s,n].indexOf(D),$=null!=(se=y?.[g])?se:0,V=M?G:l-d[j]-p[j]-$+m.altAxis,H=M?l+d[j]+p[j]-$-m.altAxis:Y,R=u&&M?function(e,t,n){var s=U(e,t,n);return s>n?n:s}(V,l,H):U(u?V:G,l,u?H:Y);v[g]=R,S[g]=R-l}t.modifiersData[ve]=S}},requiresIfExists:["offset"]};function Os(e,t,n){void 0===n&&(n=!1);var s,d,u=c(t),m=c(t)&&function(e){var t=e.getBoundingClientRect(),n=M(t.width)/e.offsetWidth||1,s=M(t.height)/e.offsetHeight||1;return 1!==n||1!==s}(t),a=w(t),i=k(e,m,n),l={scrollLeft:0,scrollTop:0},o={x:0,y:0};return(u||!u&&!n)&&(("body"!==h(t)||Re(a))&&(l=(s=t)!==r(s)&&c(s)?{scrollLeft:(d=s).scrollLeft,scrollTop:d.scrollTop}:Be(s)),c(t)?((o=k(t,!0)).x+=t.clientLeft,o.y+=t.clientTop):a&&(o.x=Ie(a))),{x:i.left+l.scrollLeft-o.x,y:i.top+l.scrollTop-o.y,width:i.width,height:i.height}}function ws(e){var n=new Map,t=new Set,s=[];function o(e){t.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach(function(e){if(!t.has(e)){var s=n.get(e);s&&o(s)}}),s.push(e)}return e.forEach(function(e){n.set(e.name,e)}),e.forEach(function(e){t.has(e.name)||o(e)}),s}De={placement:"bottom",modifiers:[],strategy:"absolute"};function It(){for(var t=arguments.length,n=new Array(t),e=0;eNumber.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_getPopperConfig(){const e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(g.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),{...e,...o(this._config.popperConfig,[e])}}_selectMenuItem({key:e,target:n}){const s=t.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(e=>P(e));s.length&&Me(s,n,e===Yt,!s.includes(n)).focus()}static jQueryInterface(e){return this.each(function(){const t=m.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(2===e.button||"keyup"===e.type&&"Tab"!==e.key)return;const n=t.find(ms);for(const a of n){const t=m.getInstance(a);if(!t||!1===t._config.autoClose)continue;const s=e.composedPath(),o=s.includes(t._menu);if(s.includes(t._element)||"inside"===t._config.autoClose&&!o||"outside"===t._config.autoClose&&o)continue;if(t._menu.contains(e.target)&&("keyup"===e.type&&"Tab"===e.key||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;const i={relatedTarget:t._element};"click"===e.type&&(i.clickEvent=e),t._completeHide(i)}}static dataApiKeydownHandler(e){const a=/input|textarea/i.test(e.target.tagName),s="Escape"===e.key,o=[ps,Yt].includes(e.key);if(!o&&!s)return;if(a&&!s)return;e.preventDefault();const i=this.matches(C)?this:t.prev(this,C)[0]||t.next(this,C)[0]||t.findOne(C,e.delegateTarget.parentNode),n=m.getOrCreateInstance(i);if(o)return e.stopPropagation(),n.show(),void n._selectMenuItem(e);n._isShown()&&(e.stopPropagation(),n.hide(),i.focus())}}e.on(document,Xt,C,m.dataApiKeydownHandler),e.on(document,Xt,te,m.dataApiKeydownHandler),e.on(document,Gt,m.clearMenus),e.on(document,"keyup.bs.dropdown.data-api",m.clearMenus),e.on(document,Gt,C,function(e){e.preventDefault(),m.getOrCreateInstance(this).toggle()}),l(m);const un="show",hn="mousedown.bs.backdrop",In={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Hn={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class pn extends G{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return In}static get DefaultType(){return Hn}static get NAME(){return"backdrop"}show(e){if(!this._config.isVisible)return void o(e);this._append();const t=this._getElement();this._config.isAnimated&&$(t),t.classList.add(un),this._emulateAnimation(()=>{o(e)})}hide(e){this._config.isVisible?(this._getElement().classList.remove(un),this._emulateAnimation(()=>{this.dispose(),o(e)})):o(e)}dispose(){this._isAppended&&(e.off(this._element,hn),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add("fade"),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=j(e.rootElement),e}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),e.on(t,hn,()=>{o(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){nn(e,this._getElement(),this._config.isAnimated)}}const gn=".bs.focustrap",vn="backward",Pn={autofocus:!0,trapElement:null},Rn={autofocus:"boolean",trapElement:"element"};class yn extends G{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Pn}static get DefaultType(){return Rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),e.off(document,gn),e.on(document,"focusin.bs.focustrap",e=>this._handleFocusin(e)),e.on(document,"keydown.tab.bs.focustrap",e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,e.off(document,gn))}_handleFocusin(e){const{trapElement:n}=this._config;if(e.target===document||e.target===n||n.contains(e.target))return;const s=t.focusableChildren(n);0===s.length?n.focus():this._lastTabNavDirection===vn?s[s.length-1].focus():s[0].focus()}_handleKeydown(e){"Tab"===e.key&&(this._lastTabNavDirection=e.shiftKey?vn:"forward")}}const _n=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",wn=".sticky-top",se="padding-right",xn="margin-right";class Ce{constructor(){this._element=document.body}getWidth(){const e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){const e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,se,t=>t+e),this._setElementAttributes(_n,se,t=>t+e),this._setElementAttributes(wn,xn,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,se),this._resetElementAttributes(_n,se),this._resetElementAttributes(wn,xn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,n){const s=this.getWidth();this._applyManipulationCallback(e,e=>{if(e!==this._element&&window.innerWidth>e.clientWidth+s)return;this._saveInitialAttribute(e,t);const o=window.getComputedStyle(e).getPropertyValue(t);e.style.setProperty(t,`${n(Number.parseFloat(o))}px`)})}_saveInitialAttribute(e,t){const n=e.style.getPropertyValue(t);n&&g.setDataAttribute(e,t,n)}_resetElementAttributes(e,t){this._applyManipulationCallback(e,e=>{const n=g.getDataAttribute(e,t);null!==n?(g.removeDataAttribute(e,t),e.style.setProperty(t,n)):e.style.removeProperty(t)})}_applyManipulationCallback(e,n){if(v(e))n(e);else for(const s of t.find(e,this._element))n(s)}}const En=".bs.modal",kn="hidden.bs.modal",An="show.bs.modal",Sn="modal-open",Mn="show",we="modal-static",Ln={backdrop:!0,focus:!0,keyboard:!0},Nn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class T extends u{constructor(e,n){super(e,n),this._dialog=t.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new Ce,this._addEventListeners()}static get Default(){return Ln}static get DefaultType(){return Nn}static get NAME(){return"modal"}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){this._isShown||this._isTransitioning||e.trigger(this._element,An,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Sn),this._adjustDialog(),this._backdrop.show(()=>this._showElement(t)))}hide(){this._isShown&&!this._isTransitioning&&(e.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Mn),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())))}dispose(){e.off(window,En),e.off(this._dialog,En),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new pn({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new yn({trapElement:this._element})}_showElement(n){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const s=t.findOne(".modal-body",this._dialog);s&&(s.scrollTop=0),$(this._element),this._element.classList.add(Mn),this._queueCallback(()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,e.trigger(this._element,"shown.bs.modal",{relatedTarget:n})},this._dialog,this._isAnimated())}_addEventListeners(){e.on(this._element,"keydown.dismiss.bs.modal",e=>{"Escape"===e.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())}),e.on(window,"resize.bs.modal",()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),e.on(this._element,"mousedown.dismiss.bs.modal",t=>{e.one(this._element,"click.dismiss.bs.modal",e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Sn),this._resetAdjustments(),this._scrollBar.reset(),e.trigger(this._element,kn)})}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(e.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const n=this._element.scrollHeight>document.documentElement.clientHeight,t=this._element.style.overflowY;"hidden"===t||this._element.classList.contains(we)||(n||(this._element.style.overflowY="hidden"),this._element.classList.add(we),this._queueCallback(()=>{this._element.classList.remove(we),this._queueCallback(()=>{this._element.style.overflowY=t},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),n=e>0;if(n&&!t){const t=d()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!n&&t){const t=d()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){const n=T.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e](t)}})}}e.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',function(n){const s=t.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),e.one(s,An,t=>{t.defaultPrevented||e.one(s,kn,()=>{P(this)&&this.focus()})});const o=t.findOne(".modal.show");o&&T.getInstance(o).hide(),T.getOrCreateInstance(s).toggle(this)}),ue(T),l(T);const Et="show",Tn="showing",jn="hiding",bn=".offcanvas.show",fn="hidePrevented.bs.offcanvas",mn="hidden.bs.offcanvas",Bn={backdrop:!0,keyboard:!0,scroll:!1},Vn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class _ extends u{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Bn}static get DefaultType(){return Vn}static get NAME(){return"offcanvas"}toggle(e){return this._isShown?this.hide():this.show(e)}show(t){this._isShown||e.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new Ce).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Tn),this._queueCallback(()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Et),this._element.classList.remove(Tn),e.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})},this._element,!0))}hide(){this._isShown&&(e.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(jn),this._backdrop.hide(),this._queueCallback(()=>{this._element.classList.remove(Et,jn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new Ce).reset(),e.trigger(this._element,mn)},this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new pn({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():e.trigger(this._element,fn)}:null})}_initializeFocusTrap(){return new yn({trapElement:this._element})}_addEventListeners(){e.on(this._element,"keydown.dismiss.bs.offcanvas",t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():e.trigger(this._element,fn))})}static jQueryInterface(e){return this.each(function(){const t=_.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}e.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',function(n){const s=t.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),b(this))return;e.one(s,mn,()=>{P(this)&&this.focus()});const o=t.findOne(bn);o&&o!==s&&_.getInstance(o).hide(),_.getOrCreateInstance(s).toggle(this)}),e.on(window,"load.bs.offcanvas.data-api",()=>{for(const e of t.find(bn))_.getOrCreateInstance(e).show()}),e.on(window,"resize.bs.offcanvas",()=>{for(const e of t.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(e).position&&_.getOrCreateInstance(e).hide()}),ue(_),l(_);const ln={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Un=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Kn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,qn=(e,t)=>{const n=e.nodeName.toLowerCase();return t.includes(n)?!Un.has(n)||Boolean(Kn.test(e.nodeValue)):t.filter(e=>e instanceof RegExp).some(e=>e.test(n))},Yn={allowList:ln,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
    "},Gn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Xn={entry:"(string|element|function|null)",selector:"(string|element)"};class Qn extends G{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return Yn}static get DefaultType(){return Gn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content={...this._config.content,...e},this}toHtml(){const e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(const[t,n]of Object.entries(this._config.content))this._setContent(e,n,t);const t=e.children[0],n=this._resolvePossibleFunction(this._config.extraClass);return n&&t.classList.add(...n.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(const[t,n]of Object.entries(e))super._typeCheckConfig({selector:t,entry:n},Xn)}_setContent(e,n,s){const o=t.findOne(s,e);o&&((n=this._resolvePossibleFunction(n))?v(n)?this._putElementInTemplate(j(n),o):this._config.html?o.innerHTML=this._maybeSanitize(n):o.textContent=n:o.remove())}_maybeSanitize(e){return this._config.sanitize?function(e,t,n){if(!e.length)return e;if(n&&"function"==typeof n)return n(e);const s=(new window.DOMParser).parseFromString(e,"text/html"),o=[].concat(...s.body.querySelectorAll("*"));for(const e of o){const n=e.nodeName.toLowerCase();if(!Object.keys(t).includes(n)){e.remove();continue}const s=[].concat(...e.attributes),i=[].concat(t["*"]||[],t[n]||[]);for(const t of s)qn(t,i)||e.removeAttribute(t.nodeName)}return s.body.innerHTML}(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return o(e,[this])}_putElementInTemplate(e,t){if(this._config.html)return t.innerHTML="",void t.append(e);t.textContent=e.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),Se="fade",oe="show",rn=".modal",an="hide.bs.modal",ie="hover",sn="focus",is={AUTO:"auto",TOP:"top",RIGHT:d()?"left":"right",BOTTOM:"bottom",LEFT:d()?"right":"left"},rs={allowList:ln,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},cs={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class D extends u{constructor(e,t){if(void 0===Ut)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return rs}static get DefaultType(){return cs}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),e.off(this._element.closest(rn),an,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const n=e.trigger(this._element,this.constructor.eventName("show")),s=(Rt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(n.defaultPrevented||!s)return;this._disposePopper();const t=this._getTipElement();this._element.setAttribute("aria-describedby",t.getAttribute("id"));const{container:o}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(o.append(t),e.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(t),t.classList.add(oe),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.on(t,"mouseover",he);this._queueCallback(()=>{e.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1},this.tip,this._isAnimated())}hide(){if(this._isShown()&&!e.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(oe),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))e.off(t,"mouseover",he);this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this._isHovered=null,this._queueCallback(()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),e.trigger(this._element,this.constructor.eventName("hidden")))},this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){const t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(Se,oe),t.classList.add(`bs-${this.constructor.NAME}-auto`);const n=(e=>{do e+=Math.floor(1e6*Math.random());while(document.getElementById(e))return e})(this.constructor.NAME).toString();return t.setAttribute("id",n),this._isAnimated()&&t.classList.add(Se),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new Qn({...this._config,content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Se)}_isShown(){return this.tip&&this.tip.classList.contains(oe)}_createPopper(e){const t=o(this._config.placement,[this,e,this._element]),n=is[t.toUpperCase()];return ze(this._element,e,this._getPopperConfig(n))}_getOffset(){const{offset:e}=this._config;return"string"==typeof e?e.split(",").map(e=>Number.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_resolvePossibleFunction(e){return o(e,[this._element])}_getPopperConfig(e){const t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:e=>{this._getTipElement().setAttribute("data-popper-placement",e.state.placement)}}]};return{...t,...o(this._config.popperConfig,[t])}}_setListeners(){const t=this._config.trigger.split(" ");for(const n of t)if("click"===n)e.on(this._element,this.constructor.eventName("click"),this._config.selector,e=>{this._initializeOnDelegatedTarget(e).toggle()});else if("manual"!==n){const t=n===ie?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),s=n===ie?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");e.on(this._element,t,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusin"===e.type?sn:ie]=!0,t._enter()}),e.on(this._element,s,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusout"===e.type?sn:ie]=t._element.contains(e.relatedTarget),t._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},e.on(this._element.closest(rn),an,this._hideModalHandler)}_fixTitle(){const e=this._element.getAttribute("title");e&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){const t=g.getDataAttributes(this._element);for(const e of Object.keys(t))Zn.has(e)&&delete t[e];return e={...t,..."object"==typeof e&&e?e:{}},e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=!1===e.container?document.body:j(e.container),"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),e}_getDelegateConfig(){const e={};for(const[t,n]of Object.entries(this._config))this.constructor.Default[t]!==n&&(e[t]=n);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){const t=D.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}l(D);const ds={...D.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},us={...D.DefaultType,content:"(null|string|element|function)"};class Fe extends D{static get Default(){return ds}static get DefaultType(){return us}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){const t=Fe.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}l(Fe);const Jt="click.bs.scrollspy",W="active",qt="[href]",gs={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},vs={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class de extends u{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return gs}static get DefaultType(){return vs}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=j(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,"string"==typeof e.threshold&&(e.threshold=e.threshold.split(",").map(e=>Number.parseFloat(e))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(e.off(this._config.target,Jt),e.on(this._config.target,Jt,qt,e=>{const t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();const n=this._rootElement||window,s=t.offsetTop-this._element.offsetTop;if(n.scrollTo)return void n.scrollTo({top:s,behavior:"smooth"});n.scrollTop=s}}))}_getNewObserver(){const e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(e=>this._observerCallback(e),e)}_observerCallback(e){const n=e=>this._targetLinks.get(`#${e.target.id}`),s=e=>{this._previousScrollData.visibleEntryTop=e.target.offsetTop,this._process(n(e))},t=(this._rootElement||document.documentElement).scrollTop,o=t>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=t;for(const i of e){if(!i.isIntersecting){this._activeTarget=null,this._clearActiveClass(n(i));continue}const a=i.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(s(i),!t)return}else o||a||s(i)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const e=t.find(qt,this._config.target);for(const n of e){if(!n.hash||b(n))continue;const s=t.findOne(decodeURI(n.hash),this._element);P(s)&&(this._targetLinks.set(decodeURI(n.hash),n),this._observableSections.set(n.hash,s))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(W),this._activateParents(t),e.trigger(this._element,"activate.bs.scrollspy",{relatedTarget:t}))}_activateParents(e){if(e.classList.contains("dropdown-item"))t.findOne(".dropdown-toggle",e.closest(".dropdown")).classList.add(W);else for(const n of t.parents(e,".nav, .list-group"))for(const e of t.prev(n,".nav-link, .nav-item > .nav-link, .list-group-item"))e.classList.add(W)}_clearActiveClass(e){e.classList.remove(W);const n=t.find("[href].active",e);for(const e of n)e.classList.remove(W)}static jQueryInterface(e){return this.each(function(){const t=de.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}e.on(window,"load.bs.scrollspy.data-api",()=>{for(const e of t.find('[data-bs-spy="scroll"]'))de.getOrCreateInstance(e)}),l(de);const js="ArrowLeft",Vt="ArrowRight",_s="ArrowUp",Pt="ArrowDown",fe="active",St="fade",qe="show",Wt='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Xe=`.nav-link:not(.dropdown-toggle), .list-group-item:not(.dropdown-toggle), [role="tab"]:not(.dropdown-toggle), ${Wt}`;class H extends u{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),e.on(this._element,"keydown.bs.tab",e=>this._keydown(e)))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const n=this._getActiveElem(),s=n?e.trigger(n,"hide.bs.tab",{relatedTarget:t}):null;e.trigger(t,"show.bs.tab",{relatedTarget:n}).defaultPrevented||s&&s.defaultPrevented||(this._deactivate(n,t),this._activate(t,n))}_activate(n,s){n&&(n.classList.add(fe),this._activate(t.getElementFromSelector(n)),this._queueCallback(()=>{"tab"===n.getAttribute("role")?(n.removeAttribute("tabindex"),n.setAttribute("aria-selected",!0),this._toggleDropDown(n,!0),e.trigger(n,"shown.bs.tab",{relatedTarget:s})):n.classList.add(qe)},n,n.classList.contains(St)))}_deactivate(n,s){n&&(n.classList.remove(fe),n.blur(),this._deactivate(t.getElementFromSelector(n)),this._queueCallback(()=>{"tab"===n.getAttribute("role")?(n.setAttribute("aria-selected",!1),n.setAttribute("tabindex","-1"),this._toggleDropDown(n,!1),e.trigger(n,"hidden.bs.tab",{relatedTarget:s})):n.classList.remove(qe)},n,n.classList.contains(St)))}_keydown(e){if(![js,Vt,_s,Pt].includes(e.key))return;e.stopPropagation(),e.preventDefault();const n=[Vt,Pt].includes(e.key),t=Me(this._getChildren().filter(e=>!b(e)),e.target,n,!0);t&&(t.focus({preventScroll:!0}),H.getOrCreateInstance(t).show())}_getChildren(){return t.find(Xe,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(const e of t)this._setInitialAttributesOnChild(e)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);const t=this._elemIsActive(e),n=this._getOuterElement(e);e.setAttribute("aria-selected",t),n!==e&&this._setAttributeIfNotExists(n,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){const n=t.getElementFromSelector(e);n&&(this._setAttributeIfNotExists(n,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(n,"aria-labelledby",`${e.id}`))}_toggleDropDown(e,n){const s=this._getOuterElement(e);if(!s.classList.contains("dropdown"))return;const o=(e,o)=>{const i=t.findOne(e,s);i&&i.classList.toggle(o,n)};o(".dropdown-toggle",fe),o(".dropdown-menu",qe),s.setAttribute("aria-expanded",n)}_setAttributeIfNotExists(e,t,n){e.hasAttribute(t)||e.setAttribute(t,n)}_elemIsActive(e){return e.classList.contains(fe)}_getInnerElement(e){return e.matches(Xe)?e:t.findOne(Xe,e)}_getOuterElement(e){return e.closest(".nav-item, .list-group-item")||e}static jQueryInterface(e){return this.each(function(){const t=H.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}e.on(document,"click.bs.tab",Wt,function(e){["A","AREA"].includes(this.tagName)&&e.preventDefault(),b(this)||H.getOrCreateInstance(this).show()}),e.on(window,"load.bs.tab",()=>{for(const e of t.find('.active[data-bs-toggle="tab"], .active[data-bs-toggle="pill"], .active[data-bs-toggle="list"]'))H.getOrCreateInstance(e)}),l(H);const yt="hide",pe="show",ge="showing",Ts={animation:"boolean",autohide:"boolean",delay:"number"},zs={animation:!0,autohide:!0,delay:5e3};class ve extends u{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return zs}static get DefaultType(){return Ts}static get NAME(){return"toast"}show(){e.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(yt),$(this._element),this._element.classList.add(pe,ge),this._queueCallback(()=>{this._element.classList.remove(ge),e.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()},this._element,this._config.animation))}hide(){this.isShown()&&(e.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(ge),this._queueCallback(()=>{this._element.classList.add(yt),this._element.classList.remove(ge,pe),e.trigger(this._element,"hidden.bs.toast")},this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(pe),super.dispose()}isShown(){return this._element.classList.contains(pe)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":this._hasMouseInteraction=t;break;case"focusin":case"focusout":this._hasKeyboardInteraction=t}if(t)return void this._clearTimeout();const n=e.relatedTarget;this._element===n||this._element.contains(n)||this._maybeScheduleHide()}_setListeners(){e.on(this._element,"mouseover.bs.toast",e=>this._onInteraction(e,!0)),e.on(this._element,"mouseout.bs.toast",e=>this._onInteraction(e,!1)),e.on(this._element,"focusin.bs.toast",e=>this._onInteraction(e,!0)),e.on(this._element,"focusout.bs.toast",e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){const t=ve.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e](this)}})}}return ue(ve),l(ve),{Alert:re,Button:ae,Carousel:K,Collapse:V,Dropdown:m,Modal:T,Offcanvas:_,Popover:Fe,ScrollSpy:de,Tab:H,Toast:ve,Tooltip:D}})}}),d=c(l())})() \ No newline at end of file diff --git a/theme/public/main.723cfbfbcdd177190468cf54d78a1ab7380b207d95d1be2db9e4c516fee2f867426188c9cf8e88f0b6daebae83c2530d1195a473b51e704c4f7668f52918a6d4.css b/theme/public/main.723cfbfbcdd177190468cf54d78a1ab7380b207d95d1be2db9e4c516fee2f867426188c9cf8e88f0b6daebae83c2530d1195a473b51e704c4f7668f52918a6d4.css new file mode 100644 index 0000000000000000000000000000000000000000..b8b5d1be33637c4aa1f0fa7183dd3cb85f554744 --- /dev/null +++ b/theme/public/main.723cfbfbcdd177190468cf54d78a1ab7380b207d95d1be2db9e4c516fee2f867426188c9cf8e88f0b6daebae83c2530d1195a473b51e704c4f7668f52918a6d4.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.0 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue: #0d6efd;--bs-indigo: #6610f2;--bs-purple: #5d2f86;--bs-pink: #d63384;--bs-red: #dc3545;--bs-orange: #fd7e14;--bs-yellow: #ffe000;--bs-green: #198754;--bs-teal: #20c997;--bs-cyan: #0dcaf0;--bs-black: #1d2d35;--bs-white: #fff;--bs-gray: #6c757d;--bs-gray-dark: #343a40;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #343a40;--bs-gray-900: #212529;--bs-primary: #5d2f86;--bs-secondary: #6c757d;--bs-success: #198754;--bs-info: #0dcaf0;--bs-warning: #ffe000;--bs-danger: #dc3545;--bs-light: #f8f9fa;--bs-dark: #212529;--bs-primary-rgb: 93,47,134;--bs-secondary-rgb: 108,117,125;--bs-success-rgb: 25,135,84;--bs-info-rgb: 13,202,240;--bs-warning-rgb: 255,224,0;--bs-danger-rgb: 220,53,69;--bs-light-rgb: 248,249,250;--bs-dark-rgb: 33,37,41;--bs-primary-text-emphasis: #251336;--bs-secondary-text-emphasis: #2b2f32;--bs-success-text-emphasis: #0a3622;--bs-info-text-emphasis: #055160;--bs-warning-text-emphasis: #665a00;--bs-danger-text-emphasis: #58151c;--bs-light-text-emphasis: #495057;--bs-dark-text-emphasis: #495057;--bs-primary-bg-subtle: #dfd5e7;--bs-secondary-bg-subtle: #e2e3e5;--bs-success-bg-subtle: #d1e7dd;--bs-info-bg-subtle: #cff4fc;--bs-warning-bg-subtle: #fff9cc;--bs-danger-bg-subtle: #f8d7da;--bs-light-bg-subtle: #fcfcfd;--bs-dark-bg-subtle: #ced4da;--bs-primary-border-subtle: #beaccf;--bs-secondary-border-subtle: #c4c8cb;--bs-success-border-subtle: #a3cfbb;--bs-info-border-subtle: #9eeaf9;--bs-warning-border-subtle: #fff399;--bs-danger-border-subtle: #f1aeb5;--bs-light-border-subtle: #e9ecef;--bs-dark-border-subtle: #adb5bd;--bs-white-rgb: 255,255,255;--bs-black-rgb: 29,45,53;--bs-font-sans-serif: "Jost", -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace: sfmono-regular, menlo, monaco, consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255,255,255,0.15), rgba(255,255,255,0));--bs-body-font-family: "Jost", -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-body-font-size:1rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #1d2d35;--bs-body-color-rgb: 29,45,53;--bs-body-bg: #fff;--bs-body-bg-rgb: 255,255,255;--bs-emphasis-color: #1d2d35;--bs-emphasis-color-rgb: 29,45,53;--bs-secondary-color: rgba(29,45,53,0.75);--bs-secondary-color-rgb: 29,45,53;--bs-secondary-bg: #e9ecef;--bs-secondary-bg-rgb: 233,236,239;--bs-tertiary-color: rgba(29,45,53,0.5);--bs-tertiary-color-rgb: 29,45,53;--bs-tertiary-bg: #f8f9fa;--bs-tertiary-bg-rgb: 248,249,250;--bs-heading-color: inherit;--bs-link-color: #5d2f86;--bs-link-color-rgb: 93,47,134;--bs-link-decoration: none;--bs-link-hover-color: #4a266b;--bs-link-hover-color-rgb: 74,38,107;--bs-code-color: #d63384;--bs-highlight-bg: #fff9cc;--bs-border-width: 1px;--bs-border-style: solid;--bs-border-color: #e9ecef;--bs-border-color-translucent: rgba(29,45,53,0.175);--bs-border-radius: .375rem;--bs-border-radius-sm: .25rem;--bs-border-radius-lg: .5rem;--bs-border-radius-xl: 1rem;--bs-border-radius-xxl: 2rem;--bs-border-radius-2xl: var(--bs-border-radius-xxl);--bs-border-radius-pill: 50rem;--bs-box-shadow: 0 0.5rem 1rem rgba(29,45,53,0.15);--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(29,45,53,0.075);--bs-box-shadow-lg: 0 1rem 3rem rgba(29,45,53,0.175);--bs-box-shadow-inset: inset 0 1px 2px rgba(29,45,53,0.075);--bs-focus-ring-width: .25rem;--bs-focus-ring-opacity: .25;--bs-focus-ring-color: rgba(93,47,134,0.25);--bs-form-valid-color: #198754;--bs-form-valid-border-color: #198754;--bs-form-invalid-color: #dc3545;--bs-form-invalid-border-color: #dc3545}*,*::before,*::after{box-sizing:border-box}@media (prefers-reduced-motion: no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(29,45,53,0)}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:700;line-height:1.2;color:var(--bs-heading-color)}h1,.h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width: 1200px){h1,.h1{font-size:2.5rem}}h2,.h2{font-size:calc(1.325rem + .9vw)}@media (min-width: 1200px){h2,.h2{font-size:2rem}}h3,.h3{font-size:calc(1.3rem + .6vw)}@media (min-width: 1200px){h3,.h3{font-size:1.75rem}}h4,.h4{font-size:calc(1.275rem + .3vw)}@media (min-width: 1200px){h4,.h4{font-size:1.5rem}}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}ol,ul{padding-left:2rem}ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}blockquote{margin:0 0 1rem}strong{font-weight:bolder}small,.small{font-size:.875em}sub{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}a{color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));text-decoration:none}a:hover{--bs-link-color-rgb: var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button{text-transform:none}[role="button"]{cursor:pointer}[list]:not([type="date"]):not([type="datetime-local"]):not([type="month"]):not([type="week"]):not([type="time"])::-webkit-calendar-picker-indicator{display:none !important}button,[type="button"]{-webkit-appearance:button}button:not(:disabled),[type="button"]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type="search"]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}.lead{font-size:1.25rem;font-weight:400}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.img-fluid{max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-xxl{--bs-gutter-x: 48px;--bs-gutter-y: 0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1240px}}@media (min-width: 1400px){.container-xxl,.container{max-width:2000px}}:root{--bs-breakpoint-xs: 0;--bs-breakpoint-sm: 576px;--bs-breakpoint-md: 768px;--bs-breakpoint-lg: 992px;--bs-breakpoint-xl: 1200px;--bs-breakpoint-xxl: 1400px}.row{--bs-gutter-x: 48px;--bs-gutter-y: 0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-1>*{flex:0 0 auto;width:100%}@media (min-width: 768px){.row-cols-md-2>*{flex:0 0 auto;width:50%}.col-md-8{flex:0 0 auto;width:50%}.col-md-12{flex:0 0 auto;width:75%}.col-md-13{flex:0 0 auto;width:81.25%}}@media (min-width: 992px){.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.col-lg-4{flex:0 0 auto;width:25%}.col-lg-5{flex:0 0 auto;width:31.25%}.col-lg-8{flex:0 0 auto;width:50%}.col-lg-9{flex:0 0 auto;width:56.25%}.col-lg-10{flex:0 0 auto;width:62.5%}.col-lg-11{flex:0 0 auto;width:68.75%}.g-lg-5{--bs-gutter-x: 3rem}.g-lg-5{--bs-gutter-y: 3rem}}@media (min-width: 1200px){.col-xl{flex:1 0 0%}.col-xl-3{flex:0 0 auto;width:18.75%}.col-xl-4{flex:0 0 auto;width:25%}.col-xl-8{flex:0 0 auto;width:50%}.col-xl-9{flex:0 0 auto;width:56.25%}}.table,table{--bs-table-color-type: initial;--bs-table-bg-type: initial;--bs-table-color-state: initial;--bs-table-bg-state: initial;--bs-table-color: var(--bs-body-color);--bs-table-bg: var(--bs-body-bg);--bs-table-border-color: var(--bs-border-color);--bs-table-accent-bg: rgba(0,0,0,0);--bs-table-striped-color: var(--bs-body-color);--bs-table-striped-bg: rgba(29,45,53,0.05);--bs-table-active-color: var(--bs-body-color);--bs-table-active-bg: rgba(29,45,53,0.1);--bs-table-hover-color: var(--bs-body-color);--bs-table-hover-bg: rgba(29,45,53,0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*,table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)))}.table>tbody,table>tbody{vertical-align:inherit}.table>thead,table>thead{vertical-align:bottom}[data-dark-mode] body table{--bs-table-color: #fff;--bs-table-bg: #212529;--bs-table-border-color: #373b3e;--bs-table-striped-bg: #2c3034;--bs-table-striped-color: #fff;--bs-table-active-bg: #373b3e;--bs-table-active-color: #fff;--bs-table-hover-bg: #323539;--bs-table-hover-color: #fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--bs-border-radius);transition:border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#ae97c3;outline:0;box-shadow:0 0 0 .25rem rgba(93,47,134,0.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.btn{--bs-btn-padding-x: .75rem;--bs-btn-padding-y: .375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: var(--bs-body-color);--bs-btn-bg: transparent;--bs-btn-border-width: var(--bs-border-width);--bs-btn-border-color: transparent;--bs-btn-border-radius: var(--bs-border-radius);--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(29,45,53,0.075);--bs-btn-disabled-opacity: .65;--bs-btn-focus-box-shadow: 0 0 0 0 rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn:disabled,.btn.disabled{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color: #fff;--bs-btn-bg: #5d2f86;--bs-btn-border-color: #5d2f86;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #4f2872;--bs-btn-hover-border-color: #4a266b;--bs-btn-focus-shadow-rgb: 117,78,152;--bs-btn-active-color: #fff;--bs-btn-active-bg: #4a266b;--bs-btn-active-border-color: #462365;--bs-btn-active-shadow: inset 0 3px 5px rgba(29,45,53,0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #5d2f86;--bs-btn-disabled-border-color: #5d2f86}.btn-light{--bs-btn-color: #1d2d35;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #1d2d35;--bs-btn-hover-bg: #d3d4d5;--bs-btn-hover-border-color: #c6c7c8;--bs-btn-focus-shadow-rgb: 215,218,220;--bs-btn-active-color: #1d2d35;--bs-btn-active-bg: #c6c7c8;--bs-btn-active-border-color: #babbbc;--bs-btn-active-shadow: inset 0 3px 5px rgba(29,45,53,0.125);--bs-btn-disabled-color: #1d2d35;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-outline-primary{--bs-btn-color: #5d2f86;--bs-btn-border-color: #5d2f86;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #5d2f86;--bs-btn-hover-border-color: #5d2f86;--bs-btn-focus-shadow-rgb: 93,47,134;--bs-btn-active-color: #fff;--bs-btn-active-bg: #5d2f86;--bs-btn-active-border-color: #5d2f86;--bs-btn-active-shadow: inset 0 3px 5px rgba(29,45,53,0.125);--bs-btn-disabled-color: #5d2f86;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #5d2f86;--bs-gradient: none}.btn-link{--bs-btn-font-weight: 400;--bs-btn-color: var(--bs-link-color);--bs-btn-bg: transparent;--bs-btn-border-color: transparent;--bs-btn-hover-color: var(--bs-link-hover-color);--bs-btn-hover-border-color: transparent;--bs-btn-active-color: var(--bs-link-hover-color);--bs-btn-active-border-color: transparent;--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-border-color: transparent;--bs-btn-box-shadow: 0 0 0 #000;--bs-btn-focus-shadow-rgb: 117,78,152;text-decoration:none}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-sm{--bs-btn-padding-y: .25rem;--bs-btn-padding-x: .5rem;--bs-btn-font-size:.875rem;--bs-btn-border-radius: var(--bs-border-radius-sm)}.fade{transition:opacity 0.15s linear}@media (prefers-reduced-motion: reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex: 1000;--bs-dropdown-min-width: 10rem;--bs-dropdown-padding-x: 0;--bs-dropdown-padding-y: .5rem;--bs-dropdown-spacer: .125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color: var(--bs-body-color);--bs-dropdown-bg: var(--bs-body-bg);--bs-dropdown-border-color: var(--bs-border-color-translucent);--bs-dropdown-border-radius: var(--bs-border-radius);--bs-dropdown-border-width: var(--bs-border-width);--bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg: var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y: .5rem;--bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(29,45,53,0.15);--bs-dropdown-link-color: var(--bs-body-color);--bs-dropdown-link-hover-color: var(--bs-body-color);--bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #5d2f86;--bs-dropdown-link-disabled-color: var(--bs-tertiary-color);--bs-dropdown-item-padding-x: 1rem;--bs-dropdown-item-padding-y: .25rem;--bs-dropdown-header-color: #6c757d;--bs-dropdown-header-padding-x: 1rem;--bs-dropdown-header-padding-y: .5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}@media (min-width: 992px){.dropdown-menu-lg-end{--bs-position: end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius, 0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color: #dee2e6;--bs-dropdown-bg: #343a40;--bs-dropdown-border-color: var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color: #dee2e6;--bs-dropdown-link-hover-color: #fff;--bs-dropdown-divider-bg: var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg: rgba(255,255,255,0.15);--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #5d2f86;--bs-dropdown-link-disabled-color: #adb5bd;--bs-dropdown-header-color: #adb5bd}.nav{--bs-nav-link-padding-x: 1rem;--bs-nav-link-padding-y: .5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-link-color);--bs-nav-link-hover-color: var(--bs-link-hover-color);--bs-nav-link-disabled-color: var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);background:none;border:0;transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.nav-link{transition:none}}.nav-link:hover,.nav-link:focus{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(93,47,134,0.25)}.nav-link.disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.navbar{--bs-navbar-padding-x: 0;--bs-navbar-padding-y: .5rem;--bs-navbar-color: #1d2d35;--bs-navbar-hover-color: #5d2f86;--bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color: #5d2f86;--bs-navbar-brand-padding-y: .3125rem;--bs-navbar-brand-margin-end: 1rem;--bs-navbar-brand-font-size: 1.25rem;--bs-navbar-brand-color: #5d2f86;--bs-navbar-brand-hover-color: #5d2f86;--bs-navbar-nav-link-padding-x: .5rem;--bs-navbar-toggler-padding-y: .25rem;--bs-navbar-toggler-padding-x: .75rem;--bs-navbar-toggler-font-size: 1.25rem;--bs-navbar-toggler-icon-bg: none;--bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius: var(--bs-border-radius);--bs-navbar-toggler-focus-width: 0;--bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x: 0;--bs-nav-link-padding-y: .5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-navbar-color);--bs-nav-link-hover-color: var(--bs-navbar-hover-color);--bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}@media (min-width: 992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:transparent !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: .5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: var(--bs-border-width);--bs-card-border-color: #e9ecef;--bs-card-border-radius: var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y: .5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: var(--bs-body-bg);--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: 24px;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.breadcrumb{--bs-breadcrumb-padding-x: 0;--bs-breadcrumb-padding-y: 0;--bs-breadcrumb-margin-bottom: 1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color: var(--bs-secondary-color);--bs-breadcrumb-item-padding-x: .5rem;--bs-breadcrumb-item-active-color: var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x: .75rem;--bs-pagination-padding-y: .375rem;--bs-pagination-font-size:1rem;--bs-pagination-color: var(--bs-link-color);--bs-pagination-bg: var(--bs-body-bg);--bs-pagination-border-width: var(--bs-border-width);--bs-pagination-border-color: var(--bs-border-color);--bs-pagination-border-radius: var(--bs-border-radius);--bs-pagination-hover-color: var(--bs-link-hover-color);--bs-pagination-hover-bg: var(--bs-tertiary-bg);--bs-pagination-hover-border-color: var(--bs-border-color);--bs-pagination-focus-color: var(--bs-link-hover-color);--bs-pagination-focus-bg: var(--bs-secondary-bg);--bs-pagination-focus-box-shadow: 0 0 0 .25rem rgba(93,47,134,0.25);--bs-pagination-active-color: #fff;--bs-pagination-active-bg: #5d2f86;--bs-pagination-active-border-color: #5d2f86;--bs-pagination-disabled-color: var(--bs-secondary-color);--bs-pagination-disabled-bg: var(--bs-secondary-bg);--bs-pagination-disabled-border-color: var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out}@media (prefers-reduced-motion: reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.badge{--bs-badge-padding-x: .65em;--bs-badge-padding-y: .35em;--bs-badge-font-size:.75em;--bs-badge-font-weight: 700;--bs-badge-color: #fff;--bs-badge-border-radius: var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg: transparent;--bs-alert-padding-x: 1.5rem;--bs-alert-padding-y: 1rem;--bs-alert-margin-bottom: 0;--bs-alert-color: inherit;--bs-alert-border-color: transparent;--bs-alert-border: 0 solid var(--bs-alert-border-color);--bs-alert-border-radius: 0;--bs-alert-link-color: inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:4.5rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1.5rem}.alert-primary{--bs-alert-color: var(--bs-primary-text-emphasis);--bs-alert-bg: var(--bs-primary-bg-subtle);--bs-alert-border-color: var(--bs-primary-border-subtle);--bs-alert-link-color: var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color: var(--bs-secondary-text-emphasis);--bs-alert-bg: var(--bs-secondary-bg-subtle);--bs-alert-border-color: var(--bs-secondary-border-subtle);--bs-alert-link-color: var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color: var(--bs-success-text-emphasis);--bs-alert-bg: var(--bs-success-bg-subtle);--bs-alert-border-color: var(--bs-success-border-subtle);--bs-alert-link-color: var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color: var(--bs-info-text-emphasis);--bs-alert-bg: var(--bs-info-bg-subtle);--bs-alert-border-color: var(--bs-info-border-subtle);--bs-alert-link-color: var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color: var(--bs-warning-text-emphasis);--bs-alert-bg: var(--bs-warning-bg-subtle);--bs-alert-border-color: var(--bs-warning-border-subtle);--bs-alert-link-color: var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color: var(--bs-danger-text-emphasis);--bs-alert-bg: var(--bs-danger-bg-subtle);--bs-alert-border-color: var(--bs-danger-border-subtle);--bs-alert-link-color: var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color: var(--bs-light-text-emphasis);--bs-alert-bg: var(--bs-light-bg-subtle);--bs-alert-border-color: var(--bs-light-border-subtle);--bs-alert-link-color: var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color: var(--bs-dark-text-emphasis);--bs-alert-bg: var(--bs-dark-bg-subtle);--bs-alert-border-color: var(--bs-dark-border-subtle);--bs-alert-link-color: var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{--bs-progress-height: 1rem;--bs-progress-font-size:.75rem;--bs-progress-bg: var(--bs-secondary-bg);--bs-progress-border-radius: var(--bs-border-radius);--bs-progress-box-shadow: var(--bs-box-shadow-inset);--bs-progress-bar-color: #fff;--bs-progress-bar-bg: #5d2f86;--bs-progress-bar-transition: width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.list-group{--bs-list-group-color: var(--bs-body-color);--bs-list-group-bg: var(--bs-body-bg);--bs-list-group-border-color: var(--bs-border-color);--bs-list-group-border-width: var(--bs-border-width);--bs-list-group-border-radius: var(--bs-border-radius);--bs-list-group-item-padding-x: 1rem;--bs-list-group-item-padding-y: .5rem;--bs-list-group-action-color: var(--bs-secondary-color);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-tertiary-bg);--bs-list-group-action-active-color: var(--bs-body-color);--bs-list-group-action-active-bg: var(--bs-secondary-bg);--bs-list-group-disabled-color: var(--bs-secondary-color);--bs-list-group-disabled-bg: var(--bs-body-bg);--bs-list-group-active-color: #fff;--bs-list-group-active-bg: #5d2f86;--bs-list-group-active-border-color: #5d2f86;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.btn-close{--bs-btn-close-color: #1d2d35;--bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%231d2d35'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity: .5;--bs-btn-close-hover-opacity: .75;--bs-btn-close-focus-shadow: 0 0 0 .25rem rgba(93,47,134,0.25);--bs-btn-close-focus-opacity: 1;--bs-btn-close-disabled-opacity: .25;--bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close:disabled,.btn-close.disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}@keyframes spinner-border{to{transform:rotate(360deg) /* rtl:ignore */}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.offcanvas{--bs-offcanvas-zindex: 1045;--bs-offcanvas-width: 400px;--bs-offcanvas-height: 30vh;--bs-offcanvas-padding-x: 1rem;--bs-offcanvas-padding-y: 1rem;--bs-offcanvas-color: var(--bs-body-color);--bs-offcanvas-bg: var(--bs-body-bg);--bs-offcanvas-border-width: var(--bs-border-width);--bs-offcanvas-border-color: var(--bs-border-color-translucent);--bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(29,45,53,0.075);--bs-offcanvas-transition: transform .3s ease-in-out;--bs-offcanvas-title-line-height: 1.5}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion: reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.show:not(.hiding){transform:none}.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#1d2d35}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}@keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}@media (min-width: 992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}}.visually-hidden{width:1px !important;height:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0, 0, 0, 0) !important;white-space:nowrap !important;border:0 !important}.visually-hidden:not(caption){position:absolute !important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.d-block{display:block !important}.d-flex{display:flex !important}.d-none{display:none !important}.shadow{box-shadow:0 0.5rem 1rem rgba(29,45,53,0.15) !important}.position-relative{position:relative !important}.border-0{border:0 !important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.w-100{width:100% !important}.flex-column{flex-direction:column !important}.flex-grow-1{flex-grow:1 !important}.flex-shrink-1{flex-shrink:1 !important}.flex-wrap{flex-wrap:wrap !important}.justify-content-center{justify-content:center !important}.justify-content-between{justify-content:space-between !important}.align-items-center{align-items:center !important}.order-first{order:-1 !important}.order-0{order:0 !important}.order-2{order:2 !important}.order-3{order:3 !important}.order-last{order:6 !important}.my-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.mt-2{margin-top:.5rem !important}.mt-3{margin-top:1rem !important}.mt-4{margin-top:1.5rem !important}.me-2{margin-right:.5rem !important}.mb-1{margin-bottom:.25rem !important}.mb-2{margin-bottom:.5rem !important}.mb-4{margin-bottom:1.5rem !important}.ms-0{margin-left:0 !important}.ms-2{margin-left:.5rem !important}.ms-3{margin-left:1rem !important}.ms-auto{margin-left:auto !important}.my-n3{margin-top:-1rem !important;margin-bottom:-1rem !important}.mt-n3{margin-top:-1rem !important}.p-2{padding:.5rem !important}.p-4{padding:1.5rem !important}.py-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.pe-4{padding-right:1.5rem !important}.pb-1{padding-bottom:.25rem !important}.pb-4{padding-bottom:1.5rem !important}.ps-0{padding-left:0 !important}.ps-2{padding-left:.5rem !important}.fw-normal{font-weight:400 !important}.text-center{text-align:center !important}.text-uppercase{text-transform:uppercase !important}.text-primary{--bs-text-opacity: 1;color:rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important}.text-dark{--bs-text-opacity: 1;color:rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important}.text-body{--bs-text-opacity: 1;color:rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important}.text-muted{--bs-text-opacity: 1;color:var(--bs-secondary-color) !important}.text-black-50{--bs-text-opacity: 1;color:rgba(29,45,53,0.5) !important}.text-reset{--bs-text-opacity: 1;color:inherit !important}.bg-primary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important}.bg-light{--bs-bg-opacity: 1;background-color:rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important}.bg-white{--bs-bg-opacity: 1;background-color:rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important}.rounded{border-radius:var(--bs-border-radius) !important}.rounded-circle{border-radius:50% !important}@media (min-width: 768px){.flex-md-row{flex-direction:row !important}}@media (min-width: 992px){.d-lg-block{display:block !important}.d-lg-none{display:none !important}.flex-lg-row{flex-direction:row !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.align-items-lg-center{align-items:center !important}.order-lg-first{order:-1 !important}.order-lg-last{order:6 !important}.mt-lg-0{margin-top:0 !important}.me-lg-2{margin-right:.5rem !important}.me-lg-auto{margin-right:auto !important}.ms-lg-2{margin-left:.5rem !important}.ms-lg-auto{margin-left:auto !important}.p-lg-0{padding:0 !important}.py-lg-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.pe-lg-5{padding-right:3rem !important}.pb-lg-5{padding-bottom:3rem !important}.text-lg-end{text-align:right !important}.text-lg-center{text-align:center !important}}@media (min-width: 1200px){.d-xl-block{display:block !important}.d-xl-none{display:none !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}}@font-face{font-family:'KaTeX_AMS';src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Caligraphic';src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype");font-weight:bold;font-style:normal}@font-face{font-family:'KaTeX_Caligraphic';src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Fraktur';src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype");font-weight:bold;font-style:normal}@font-face{font-family:'KaTeX_Fraktur';src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Main';src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype");font-weight:bold;font-style:normal}@font-face{font-family:'KaTeX_Main';src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype");font-weight:bold;font-style:italic}@font-face{font-family:'KaTeX_Main';src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype");font-weight:normal;font-style:italic}@font-face{font-family:'KaTeX_Main';src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Math';src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype");font-weight:bold;font-style:italic}@font-face{font-family:'KaTeX_Math';src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype");font-weight:normal;font-style:italic}@font-face{font-family:'KaTeX_SansSerif';src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype");font-weight:bold;font-style:normal}@font-face{font-family:'KaTeX_SansSerif';src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype");font-weight:normal;font-style:italic}@font-face{font-family:'KaTeX_SansSerif';src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Script';src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Size1';src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Size2';src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Size3';src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Size4';src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}@font-face{font-family:'KaTeX_Typewriter';src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype");font-weight:normal;font-style:normal}.katex{font:normal 1.21em KaTeX_Main, Times New Roman, serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none !important}.katex *{border-color:currentColor}.katex .katex-version::after{content:"0.16.8"}.katex .katex-mathml{position:absolute;clip:rect(1px, 1px, 1px, 1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;display:inline-block;white-space:nowrap;width:-moz-min-content;width:min-content}.katex .strut{display:inline-block}.katex .textbf{font-weight:bold}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:bold}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:bold;font-style:italic}.katex .amsrm{font-family:KaTeX_AMS}.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:bold}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed;border-collapse:collapse}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .vbox{display:inline-flex;flex-direction:column;align-items:baseline}.katex .hbox{display:inline-flex;flex-direction:row;width:100%}.katex .thinbox{display:inline-flex;flex-direction:row;width:0;max-width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline,.katex .hdashline,.katex .rule{min-height:1px}.katex .mspace{display:inline-block}.katex .llap,.katex .rlap,.katex .clap{width:0;position:relative}.katex .llap>.inner,.katex .rlap>.inner,.katex .clap>.inner{position:absolute}.katex .llap>.fix,.katex .rlap>.fix,.katex .clap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .rlap>.inner,.katex .clap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:solid 0;position:relative}.katex .overline .overline-line,.katex .underline .underline-line,.katex .hline{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:0.27777778em;margin-right:-0.55555556em}.katex .sizing.reset-size1.size1,.katex .fontsize-ensurer.reset-size1.size1{font-size:1em}.katex .sizing.reset-size1.size2,.katex .fontsize-ensurer.reset-size1.size2{font-size:1.2em}.katex .sizing.reset-size1.size3,.katex .fontsize-ensurer.reset-size1.size3{font-size:1.4em}.katex .sizing.reset-size1.size4,.katex .fontsize-ensurer.reset-size1.size4{font-size:1.6em}.katex .sizing.reset-size1.size5,.katex .fontsize-ensurer.reset-size1.size5{font-size:1.8em}.katex .sizing.reset-size1.size6,.katex .fontsize-ensurer.reset-size1.size6{font-size:2em}.katex .sizing.reset-size1.size7,.katex .fontsize-ensurer.reset-size1.size7{font-size:2.4em}.katex .sizing.reset-size1.size8,.katex .fontsize-ensurer.reset-size1.size8{font-size:2.88em}.katex .sizing.reset-size1.size9,.katex .fontsize-ensurer.reset-size1.size9{font-size:3.456em}.katex .sizing.reset-size1.size10,.katex .fontsize-ensurer.reset-size1.size10{font-size:4.148em}.katex .sizing.reset-size1.size11,.katex .fontsize-ensurer.reset-size1.size11{font-size:4.976em}.katex .sizing.reset-size2.size1,.katex .fontsize-ensurer.reset-size2.size1{font-size:0.83333333em}.katex .sizing.reset-size2.size2,.katex .fontsize-ensurer.reset-size2.size2{font-size:1em}.katex .sizing.reset-size2.size3,.katex .fontsize-ensurer.reset-size2.size3{font-size:1.16666667em}.katex .sizing.reset-size2.size4,.katex .fontsize-ensurer.reset-size2.size4{font-size:1.33333333em}.katex .sizing.reset-size2.size5,.katex .fontsize-ensurer.reset-size2.size5{font-size:1.5em}.katex .sizing.reset-size2.size6,.katex .fontsize-ensurer.reset-size2.size6{font-size:1.66666667em}.katex .sizing.reset-size2.size7,.katex .fontsize-ensurer.reset-size2.size7{font-size:2em}.katex .sizing.reset-size2.size8,.katex .fontsize-ensurer.reset-size2.size8{font-size:2.4em}.katex .sizing.reset-size2.size9,.katex .fontsize-ensurer.reset-size2.size9{font-size:2.88em}.katex .sizing.reset-size2.size10,.katex .fontsize-ensurer.reset-size2.size10{font-size:3.45666667em}.katex .sizing.reset-size2.size11,.katex .fontsize-ensurer.reset-size2.size11{font-size:4.14666667em}.katex .sizing.reset-size3.size1,.katex .fontsize-ensurer.reset-size3.size1{font-size:0.71428571em}.katex .sizing.reset-size3.size2,.katex .fontsize-ensurer.reset-size3.size2{font-size:0.85714286em}.katex .sizing.reset-size3.size3,.katex .fontsize-ensurer.reset-size3.size3{font-size:1em}.katex .sizing.reset-size3.size4,.katex .fontsize-ensurer.reset-size3.size4{font-size:1.14285714em}.katex .sizing.reset-size3.size5,.katex .fontsize-ensurer.reset-size3.size5{font-size:1.28571429em}.katex .sizing.reset-size3.size6,.katex .fontsize-ensurer.reset-size3.size6{font-size:1.42857143em}.katex .sizing.reset-size3.size7,.katex .fontsize-ensurer.reset-size3.size7{font-size:1.71428571em}.katex .sizing.reset-size3.size8,.katex .fontsize-ensurer.reset-size3.size8{font-size:2.05714286em}.katex .sizing.reset-size3.size9,.katex .fontsize-ensurer.reset-size3.size9{font-size:2.46857143em}.katex .sizing.reset-size3.size10,.katex .fontsize-ensurer.reset-size3.size10{font-size:2.96285714em}.katex .sizing.reset-size3.size11,.katex .fontsize-ensurer.reset-size3.size11{font-size:3.55428571em}.katex .sizing.reset-size4.size1,.katex .fontsize-ensurer.reset-size4.size1{font-size:0.625em}.katex .sizing.reset-size4.size2,.katex .fontsize-ensurer.reset-size4.size2{font-size:0.75em}.katex .sizing.reset-size4.size3,.katex .fontsize-ensurer.reset-size4.size3{font-size:0.875em}.katex .sizing.reset-size4.size4,.katex .fontsize-ensurer.reset-size4.size4{font-size:1em}.katex .sizing.reset-size4.size5,.katex .fontsize-ensurer.reset-size4.size5{font-size:1.125em}.katex .sizing.reset-size4.size6,.katex .fontsize-ensurer.reset-size4.size6{font-size:1.25em}.katex .sizing.reset-size4.size7,.katex .fontsize-ensurer.reset-size4.size7{font-size:1.5em}.katex .sizing.reset-size4.size8,.katex .fontsize-ensurer.reset-size4.size8{font-size:1.8em}.katex .sizing.reset-size4.size9,.katex .fontsize-ensurer.reset-size4.size9{font-size:2.16em}.katex .sizing.reset-size4.size10,.katex .fontsize-ensurer.reset-size4.size10{font-size:2.5925em}.katex .sizing.reset-size4.size11,.katex .fontsize-ensurer.reset-size4.size11{font-size:3.11em}.katex .sizing.reset-size5.size1,.katex .fontsize-ensurer.reset-size5.size1{font-size:0.55555556em}.katex .sizing.reset-size5.size2,.katex .fontsize-ensurer.reset-size5.size2{font-size:0.66666667em}.katex .sizing.reset-size5.size3,.katex .fontsize-ensurer.reset-size5.size3{font-size:0.77777778em}.katex .sizing.reset-size5.size4,.katex .fontsize-ensurer.reset-size5.size4{font-size:0.88888889em}.katex .sizing.reset-size5.size5,.katex .fontsize-ensurer.reset-size5.size5{font-size:1em}.katex .sizing.reset-size5.size6,.katex .fontsize-ensurer.reset-size5.size6{font-size:1.11111111em}.katex .sizing.reset-size5.size7,.katex .fontsize-ensurer.reset-size5.size7{font-size:1.33333333em}.katex .sizing.reset-size5.size8,.katex .fontsize-ensurer.reset-size5.size8{font-size:1.6em}.katex .sizing.reset-size5.size9,.katex .fontsize-ensurer.reset-size5.size9{font-size:1.92em}.katex .sizing.reset-size5.size10,.katex .fontsize-ensurer.reset-size5.size10{font-size:2.30444444em}.katex .sizing.reset-size5.size11,.katex .fontsize-ensurer.reset-size5.size11{font-size:2.76444444em}.katex .sizing.reset-size6.size1,.katex .fontsize-ensurer.reset-size6.size1{font-size:0.5em}.katex .sizing.reset-size6.size2,.katex .fontsize-ensurer.reset-size6.size2{font-size:0.6em}.katex .sizing.reset-size6.size3,.katex .fontsize-ensurer.reset-size6.size3{font-size:0.7em}.katex .sizing.reset-size6.size4,.katex .fontsize-ensurer.reset-size6.size4{font-size:0.8em}.katex .sizing.reset-size6.size5,.katex .fontsize-ensurer.reset-size6.size5{font-size:0.9em}.katex .sizing.reset-size6.size6,.katex .fontsize-ensurer.reset-size6.size6{font-size:1em}.katex .sizing.reset-size6.size7,.katex .fontsize-ensurer.reset-size6.size7{font-size:1.2em}.katex .sizing.reset-size6.size8,.katex .fontsize-ensurer.reset-size6.size8{font-size:1.44em}.katex .sizing.reset-size6.size9,.katex .fontsize-ensurer.reset-size6.size9{font-size:1.728em}.katex .sizing.reset-size6.size10,.katex .fontsize-ensurer.reset-size6.size10{font-size:2.074em}.katex .sizing.reset-size6.size11,.katex .fontsize-ensurer.reset-size6.size11{font-size:2.488em}.katex .sizing.reset-size7.size1,.katex .fontsize-ensurer.reset-size7.size1{font-size:0.41666667em}.katex .sizing.reset-size7.size2,.katex .fontsize-ensurer.reset-size7.size2{font-size:0.5em}.katex .sizing.reset-size7.size3,.katex .fontsize-ensurer.reset-size7.size3{font-size:0.58333333em}.katex .sizing.reset-size7.size4,.katex .fontsize-ensurer.reset-size7.size4{font-size:0.66666667em}.katex .sizing.reset-size7.size5,.katex .fontsize-ensurer.reset-size7.size5{font-size:0.75em}.katex .sizing.reset-size7.size6,.katex .fontsize-ensurer.reset-size7.size6{font-size:0.83333333em}.katex .sizing.reset-size7.size7,.katex .fontsize-ensurer.reset-size7.size7{font-size:1em}.katex .sizing.reset-size7.size8,.katex .fontsize-ensurer.reset-size7.size8{font-size:1.2em}.katex .sizing.reset-size7.size9,.katex .fontsize-ensurer.reset-size7.size9{font-size:1.44em}.katex .sizing.reset-size7.size10,.katex .fontsize-ensurer.reset-size7.size10{font-size:1.72833333em}.katex .sizing.reset-size7.size11,.katex .fontsize-ensurer.reset-size7.size11{font-size:2.07333333em}.katex .sizing.reset-size8.size1,.katex .fontsize-ensurer.reset-size8.size1{font-size:0.34722222em}.katex .sizing.reset-size8.size2,.katex .fontsize-ensurer.reset-size8.size2{font-size:0.41666667em}.katex .sizing.reset-size8.size3,.katex .fontsize-ensurer.reset-size8.size3{font-size:0.48611111em}.katex .sizing.reset-size8.size4,.katex .fontsize-ensurer.reset-size8.size4{font-size:0.55555556em}.katex .sizing.reset-size8.size5,.katex .fontsize-ensurer.reset-size8.size5{font-size:0.625em}.katex .sizing.reset-size8.size6,.katex .fontsize-ensurer.reset-size8.size6{font-size:0.69444444em}.katex .sizing.reset-size8.size7,.katex .fontsize-ensurer.reset-size8.size7{font-size:0.83333333em}.katex .sizing.reset-size8.size8,.katex .fontsize-ensurer.reset-size8.size8{font-size:1em}.katex .sizing.reset-size8.size9,.katex .fontsize-ensurer.reset-size8.size9{font-size:1.2em}.katex .sizing.reset-size8.size10,.katex .fontsize-ensurer.reset-size8.size10{font-size:1.44027778em}.katex .sizing.reset-size8.size11,.katex .fontsize-ensurer.reset-size8.size11{font-size:1.72777778em}.katex .sizing.reset-size9.size1,.katex .fontsize-ensurer.reset-size9.size1{font-size:0.28935185em}.katex .sizing.reset-size9.size2,.katex .fontsize-ensurer.reset-size9.size2{font-size:0.34722222em}.katex .sizing.reset-size9.size3,.katex .fontsize-ensurer.reset-size9.size3{font-size:0.40509259em}.katex .sizing.reset-size9.size4,.katex .fontsize-ensurer.reset-size9.size4{font-size:0.46296296em}.katex .sizing.reset-size9.size5,.katex .fontsize-ensurer.reset-size9.size5{font-size:0.52083333em}.katex .sizing.reset-size9.size6,.katex .fontsize-ensurer.reset-size9.size6{font-size:0.5787037em}.katex .sizing.reset-size9.size7,.katex .fontsize-ensurer.reset-size9.size7{font-size:0.69444444em}.katex .sizing.reset-size9.size8,.katex .fontsize-ensurer.reset-size9.size8{font-size:0.83333333em}.katex .sizing.reset-size9.size9,.katex .fontsize-ensurer.reset-size9.size9{font-size:1em}.katex .sizing.reset-size9.size10,.katex .fontsize-ensurer.reset-size9.size10{font-size:1.20023148em}.katex .sizing.reset-size9.size11,.katex .fontsize-ensurer.reset-size9.size11{font-size:1.43981481em}.katex .sizing.reset-size10.size1,.katex .fontsize-ensurer.reset-size10.size1{font-size:0.24108004em}.katex .sizing.reset-size10.size2,.katex .fontsize-ensurer.reset-size10.size2{font-size:0.28929605em}.katex .sizing.reset-size10.size3,.katex .fontsize-ensurer.reset-size10.size3{font-size:0.33751205em}.katex .sizing.reset-size10.size4,.katex .fontsize-ensurer.reset-size10.size4{font-size:0.38572806em}.katex .sizing.reset-size10.size5,.katex .fontsize-ensurer.reset-size10.size5{font-size:0.43394407em}.katex .sizing.reset-size10.size6,.katex .fontsize-ensurer.reset-size10.size6{font-size:0.48216008em}.katex .sizing.reset-size10.size7,.katex .fontsize-ensurer.reset-size10.size7{font-size:0.57859209em}.katex .sizing.reset-size10.size8,.katex .fontsize-ensurer.reset-size10.size8{font-size:0.69431051em}.katex .sizing.reset-size10.size9,.katex .fontsize-ensurer.reset-size10.size9{font-size:0.83317261em}.katex .sizing.reset-size10.size10,.katex .fontsize-ensurer.reset-size10.size10{font-size:1em}.katex .sizing.reset-size10.size11,.katex .fontsize-ensurer.reset-size10.size11{font-size:1.19961427em}.katex .sizing.reset-size11.size1,.katex .fontsize-ensurer.reset-size11.size1{font-size:0.20096463em}.katex .sizing.reset-size11.size2,.katex .fontsize-ensurer.reset-size11.size2{font-size:0.24115756em}.katex .sizing.reset-size11.size3,.katex .fontsize-ensurer.reset-size11.size3{font-size:0.28135048em}.katex .sizing.reset-size11.size4,.katex .fontsize-ensurer.reset-size11.size4{font-size:0.32154341em}.katex .sizing.reset-size11.size5,.katex .fontsize-ensurer.reset-size11.size5{font-size:0.36173633em}.katex .sizing.reset-size11.size6,.katex .fontsize-ensurer.reset-size11.size6{font-size:0.40192926em}.katex .sizing.reset-size11.size7,.katex .fontsize-ensurer.reset-size11.size7{font-size:0.48231511em}.katex .sizing.reset-size11.size8,.katex .fontsize-ensurer.reset-size11.size8{font-size:0.57877814em}.katex .sizing.reset-size11.size9,.katex .fontsize-ensurer.reset-size11.size9{font-size:0.69453376em}.katex .sizing.reset-size11.size10,.katex .fontsize-ensurer.reset-size11.size10{font-size:0.83360129em}.katex .sizing.reset-size11.size11,.katex .fontsize-ensurer.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:0.12em}.katex .delimcenter{position:relative}.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex svg path{stroke:none}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy::before,.katex .stretchy::after{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 0.5em}.katex .cd-arrow-pad{padding:0 0.55556em 0 0.27778em}.katex .x-arrow,.katex .mover,.katex .munder{text-align:center}.katex .boxpad{padding:0 0.3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:0.04em solid}.katex .cancel-pad{padding:0 0.2em}.katex .cancel-lap{margin-left:-0.2em;margin-right:-0.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:0.08em}.katex .angl{box-sizing:border-box;border-top:0.049em solid;border-right:0.049em solid;margin-right:0.03889em}.katex .anglpad{padding:0 0.03889em}.katex .eqn-num::before{counter-increment:katexEqnNo;content:"(" counter(katexEqnNo) ")"}.katex .mml-eqn-num::before{counter-increment:mmlEqnNo;content:"(" counter(mmlEqnNo) ")"}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + 0.3em);text-align:left}.katex .cd-label-right{display:inline-block;position:absolute;left:calc(50% + 0.3em);text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left;padding-left:2em}body{counter-reset:katexEqnNo mmlEqnNo}@font-face{font-family:Jost;font-style:normal;font-weight:400;font-display:swap;src:local("Jost Regular Regular"),local("Jost-Regular"),local("Jost* Book"),local("Jost-Book"),url("fonts/vendor/jost/jost-v4-latin-regular.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-regular.woff") format("woff")}@font-face{font-family:Jost;font-style:normal;font-weight:500;font-display:swap;src:local("Jost Regular Medium"),local("JostRoman-Medium"),local("Jost* Medium"),local("Jost-Medium"),url("fonts/vendor/jost/jost-v4-latin-500.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-500.woff") format("woff")}@font-face{font-family:Jost;font-style:normal;font-weight:700;font-display:swap;src:local("Jost Regular Bold"),local("JostRoman-Bold"),local("Jost* Bold"),local("Jost-Bold"),url("fonts/vendor/jost/jost-v4-latin-700.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-700.woff") format("woff")}@font-face{font-family:Jost;font-style:italic;font-weight:400;font-display:swap;src:local("Jost Italic Italic"),local("Jost-Italic"),local("Jost* BookItalic"),local("Jost-BookItalic"),url("fonts/vendor/jost/jost-v4-latin-italic.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-italic.woff") format("woff")}@font-face{font-family:Jost;font-style:italic;font-weight:500;font-display:swap;src:local("Jost Italic Medium Italic"),local("JostItalic-Medium"),local("Jost* Medium Italic"),local("Jost-MediumItalic"),url("fonts/vendor/jost/jost-v4-latin-500italic.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-500italic.woff") format("woff")}@font-face{font-family:Jost;font-style:italic;font-weight:700;font-display:swap;src:local("Jost Italic Bold Italic"),local("JostItalic-Bold"),local("Jost* Bold Italic"),local("Jost-BoldItalic"),url("fonts/vendor/jost/jost-v4-latin-700italic.woff2") format("woff2"),url("fonts/vendor/jost/jost-v4-latin-700italic.woff") format("woff")}.contributors .content,.blog .content,.page .content,.error404 .content,.docs.list .content,.tutorial.list .content,.showcase.list .content,.categories.list .content,.tags.list .content{padding-top:1rem;padding-bottom:3rem}.content img{max-width:100%}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin:2rem 0 1rem}.offcanvas-header{border-bottom:1px solid #dee2e6;padding-top:1.0625rem;padding-bottom:0.8125rem}h5.offcanvas-title,.offcanvas-title.h5{margin:0}body.docs{padding-top:0 !important}@media (min-width: 768px){body{font-size:1.125rem}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:1.125rem}}.home h1,.home .h1{font-size:calc(1.875rem + 1.5vw)}a:hover,a:focus{text-decoration:underline}a.btn:hover,a.btn:focus{text-decoration:none}.section{padding-top:5rem;padding-bottom:5rem}.section-sm{padding-top:1rem;padding-bottom:1rem}.docs-sidebar{order:2}@media (min-width: 992px){.docs-sidebar{order:0;border-right:1px solid #e9ecef}@supports (position: sticky){.docs-sidebar{position:sticky;top:4rem;z-index:1000;height:calc(100vh - 4rem)}.docs-sidebar-top{top:0}}}@media (min-width: 1200px){.docs-sidebar{flex:0 1 320px}}.docs-links{padding-bottom:5rem}@media (min-width: 992px){@supports (position: sticky){.docs-links{max-height:calc(100vh - 4rem);overflow-y:scroll}}}@media (min-width: 992px){.docs-links{display:block;width:auto;margin-right:-1.5rem;padding-bottom:4rem}}.docs-toc{order:2}@supports (position: sticky){.docs-toc{position:sticky;top:4rem;height:calc(100vh - 4rem);overflow-y:auto}.docs-toc-top{top:0}}.docs-content{padding-bottom:3rem;order:1}.docs-navigation{border-top:1px solid #e9ecef;margin-top:2rem;margin-bottom:0;padding-top:2rem}.docs-navigation a{font-size:.9rem}@media (min-width: 992px){.docs-navigation{margin-bottom:-1rem}.docs-navigation a{font-size:1rem}}.navbar a:hover,.navbar a:focus{text-decoration:none}#TableOfContents ul,#toc ul{padding-left:0;list-style:none}#toc a.active{color:#5d2f86;font-weight:500}::-moz-selection{background:rgba(212,53,159,0.2)}::selection{background:rgba(212,53,159,0.2)}.bg-dots{background-image:radial-gradient(#dee2e6 15%, transparent 15%);background-position:0 0;background-size:1rem 1rem;-webkit-mask:linear-gradient(to top, #fff, transparent);mask:linear-gradient(to top, #fff, transparent);width:100%;height:9rem;margin-top:-10rem;z-index:-1}.katex{font-size:1.125rem}[data-dark-mode] body{background:#212529;color:#dee2e6}[data-dark-mode] body a{color:#8ed6fb}[data-dark-mode] body a.text-body{color:#dee2e6 !important}[data-dark-mode] body .btn-primary{--bs-btn-color: #1d2d35;--bs-btn-bg: #8ed6fb;--bs-btn-border-color: #8ed6fb;--bs-btn-hover-color: #1d2d35;--bs-btn-hover-bg: #9fdcfc;--bs-btn-hover-border-color: #99dafb;--bs-btn-focus-shadow-rgb: 125,189,221;--bs-btn-active-color: #1d2d35;--bs-btn-active-bg: #a5defc;--bs-btn-active-border-color: #99dafb;--bs-btn-active-shadow: inset 0 3px 5px rgba(29,45,53,0.125);--bs-btn-disabled-color: #1d2d35;--bs-btn-disabled-bg: #8ed6fb;--bs-btn-disabled-border-color: #8ed6fb;color:#212529 !important}[data-dark-mode] body .btn-outline-primary{--bs-btn-color: #8ed6fb;--bs-btn-border-color: #8ed6fb;--bs-btn-hover-color: #8ed6fb;--bs-btn-hover-bg: #8ed6fb;--bs-btn-hover-border-color: #8ed6fb;--bs-btn-focus-shadow-rgb: 142,214,251;--bs-btn-active-color: #1d2d35;--bs-btn-active-bg: #8ed6fb;--bs-btn-active-border-color: #8ed6fb;--bs-btn-active-shadow: inset 0 3px 5px rgba(29,45,53,0.125);--bs-btn-disabled-color: #8ed6fb;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #8ed6fb;--bs-gradient: none;color:#8ed6fb}[data-dark-mode] body .btn-outline-primary:hover{color:#212529}[data-dark-mode] body .btn-doks-light{color:#dee2e6}[data-dark-mode] body .show>.btn-doks-light,[data-dark-mode] body .btn-doks-light:hover,[data-dark-mode] body .btn-doks-light:active{color:#8ed6fb}[data-dark-mode] body .btn-menu svg{color:#dee2e6}[data-dark-mode] body .doks-sidebar-toggle{color:#dee2e6}[data-dark-mode] body .btn-menu:hover,[data-dark-mode] body .btn-doks-light:hover,[data-dark-mode] body .doks-sidebar-toggle:hover{background:#1b1f22}[data-dark-mode] body .navbar,[data-dark-mode] body .doks-subnavbar{background-color:rgba(33,37,41,0.95);border-bottom:1px solid #343a40}[data-dark-mode] body.home .navbar{border-bottom:0}[data-dark-mode] body .offcanvas-header{border-bottom:1px solid #343a40}[data-dark-mode] body .offcanvas .nav-link{color:#dee2e6}[data-dark-mode] body .offcanvas .nav-link:hover,[data-dark-mode] body .offcanvas .nav-link:focus{color:#8ed6fb}[data-dark-mode] body .offcanvas .nav-link.active{color:#8ed6fb}[data-dark-mode] body .navbar-light .navbar-brand{color:#dee2e6 !important}[data-dark-mode] body .navbar-light .navbar-nav .nav-link{color:#dee2e6}[data-dark-mode] body .navbar-light .navbar-nav .nav-link:hover,[data-dark-mode] body .navbar-light .navbar-nav .nav-link:focus{color:#8ed6fb}[data-dark-mode] body .navbar-light .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}[data-dark-mode] body .navbar-light .navbar-nav .show>.nav-link,[data-dark-mode] body .navbar-light .navbar-nav .active>.nav-link,[data-dark-mode] body .navbar-light .navbar-nav .nav-link.show,[data-dark-mode] body .navbar-light .navbar-nav .nav-link.active{color:#8ed6fb}[data-dark-mode] body .navbar-light .navbar-text{color:#dee2e6}[data-dark-mode] body .alert-primary a{color:#212529}[data-dark-mode] body .alert-doks{background:#1b1f22;color:#dee2e6}[data-dark-mode] body .alert-doks a{color:#8ed6fb}[data-dark-mode] body .page-links a{color:#dee2e6}[data-dark-mode] body .btn-toggle-nav a{color:#dee2e6}[data-dark-mode] body .showcase-meta a{color:#dee2e6}[data-dark-mode] body .showcase-meta a:hover,[data-dark-mode] body .showcase-meta a:focus{color:#8ed6fb}[data-dark-mode] body .docs-link:hover,[data-dark-mode] body .docs-link.active,[data-dark-mode] body .page-links a:hover{text-decoration:none;color:#8ed6fb}[data-dark-mode] body .btn-toggle{color:#dee2e6;background-color:transparent;border:0}[data-dark-mode] body .btn-toggle:hover,[data-dark-mode] body .btn-toggle:focus{color:#dee2e6}[data-dark-mode] body .btn-toggle::before{width:1.25em;line-height:0;content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28222, 226, 230, 0.75%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");transition:transform 0.35s ease;transform-origin:0.5em 50%;margin-bottom:0.125rem}[data-dark-mode] body .btn-toggle[aria-expanded="true"]{color:#dee2e6}[data-dark-mode] body .btn-toggle[aria-expanded="true"]::before{transform:rotate(90deg)}[data-dark-mode] body .btn-toggle-nav a:hover,[data-dark-mode] body .btn-toggle-nav a:focus{color:#8ed6fb}[data-dark-mode] body .btn-toggle-nav a.active{color:#8ed6fb}[data-dark-mode] body .navbar-light .navbar-text a{color:#8ed6fb}[data-dark-mode] body .docs-links h3.sidebar-link a,[data-dark-mode] body .docs-links .sidebar-link.h3 a,[data-dark-mode] body .page-links h3.sidebar-link a,[data-dark-mode] body .page-links .sidebar-link.h3 a{color:#dee2e6}[data-dark-mode] body .navbar-light .navbar-text a:hover,[data-dark-mode] body .navbar-light .navbar-text a:focus{color:#8ed6fb}[data-dark-mode] body .navbar .btn-link{color:#dee2e6}[data-dark-mode] body .content .btn-link{color:#8ed6fb}[data-dark-mode] body .content .btn-link:hover{color:#8ed6fb}[data-dark-mode] body .content img[src^="https://latex.codecogs.com/svg.latex"]{filter:invert(1)}[data-dark-mode] body .navbar .btn-link:hover{color:#8ed6fb}[data-dark-mode] body .navbar .btn-link:active{color:#8ed6fb}[data-dark-mode] body .form-control.is-search{background:#1b1f22;border:1px solid transparent;color:#dee2e6}[data-dark-mode] body .form-control.is-search:focus{border:1px solid #8ed6fb}[data-dark-mode] body .doks-search::after{color:#dee2e6;border:1px solid #495057}[data-dark-mode] body .text-dark{color:#dee2e6 !important}[data-dark-mode] body .form-control{color:#dee2e6}[data-dark-mode] body .form-control::-moz-placeholder{color:#ced4da;opacity:1}[data-dark-mode] body .form-control::placeholder{color:#ced4da;opacity:1}[data-dark-mode] body .border-top{border-top:1px solid #343a40 !important}@media (min-width: 992px){[data-dark-mode] body .docs-sidebar{order:0;border-right:1px solid #343a40}}[data-dark-mode] body .docs-navigation{border-top:1px solid #343a40}[data-dark-mode] body pre code::-webkit-scrollbar-thumb{background:#ced4da}[data-dark-mode] body code:not(.hljs){background:#1b1f22;color:#dee2e6}[data-dark-mode] body pre code:hover{scrollbar-width:thin;scrollbar-color:#343a40 transparent}[data-dark-mode] body pre code::-webkit-scrollbar-thumb:hover{background:#adb5bd}[data-dark-mode] body blockquote{border-left:3px solid #343a40}[data-dark-mode] body .footer{border-top:1px solid #343a40}[data-dark-mode] body .docs-links,[data-dark-mode] body .docs-toc{scrollbar-width:thin;scrollbar-color:#212529 #212529}[data-dark-mode] body .docs-links::-webkit-scrollbar,[data-dark-mode] body .docs-toc::-webkit-scrollbar{width:5px}[data-dark-mode] body .docs-links::-webkit-scrollbar-track,[data-dark-mode] body .docs-toc::-webkit-scrollbar-track{background:#212529}[data-dark-mode] body .docs-links::-webkit-scrollbar-thumb,[data-dark-mode] body .docs-toc::-webkit-scrollbar-thumb{background:#212529}[data-dark-mode] body .docs-links:hover,[data-dark-mode] body .docs-toc:hover{scrollbar-width:thin;scrollbar-color:#343a40 #212529}[data-dark-mode] body .docs-links:hover::-webkit-scrollbar-thumb,[data-dark-mode] body .docs-toc:hover::-webkit-scrollbar-thumb{background:#343a40}[data-dark-mode] body .docs-links::-webkit-scrollbar-thumb:hover,[data-dark-mode] body .docs-toc::-webkit-scrollbar-thumb:hover{background:#343a40}[data-dark-mode] body .docs-links h3:not(:first-child),[data-dark-mode] body .docs-links .h3:not(:first-child){border-top:1px solid #343a40}[data-dark-mode] body a.docs-link{color:#dee2e6}[data-dark-mode] body .page-links li:not(:first-child){border-top:1px dashed #343a40}[data-dark-mode] body .card{background:#212529;border:1px solid #343a40}[data-dark-mode] body .card.bg-light{background:#1b1f22 !important}[data-dark-mode] body .navbar .menu-icon .navicon{background:#dee2e6}[data-dark-mode] body .navbar .menu-icon .navicon::before,[data-dark-mode] body .navbar .menu-icon .navicon::after{background:#dee2e6}[data-dark-mode] body .logo-light{display:none !important}[data-dark-mode] body .logo-dark{display:inline-block !important}[data-dark-mode] body .bg-light{background:#1e2125 !important}[data-dark-mode] body .bg-dots{background-image:radial-gradient(#556370 15%, transparent 15%)}[data-dark-mode] body .text-muted{color:#c8cfd6 !important}[data-dark-mode] body .alert-primary{background:#8ed6fb;color:#212529}[data-dark-mode] body .figure-caption{color:#dee2e6}[data-dark-mode] body .copy-status::after{content:"Copy";display:block;color:#dee2e6}[data-dark-mode] body .copy-status:hover::after{content:"Copy";display:block;color:#8ed6fb}[data-dark-mode] body .copy-status:focus::after,[data-dark-mode] body .copy-status:active::after{content:"Copied";display:block;color:#8ed6fb}[data-dark-mode] body .offcanvas{background-color:#212529}[data-dark-mode] body .btn-close{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNkZWUyZTYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0iZmVhdGhlciBmZWF0aGVyLXgiPjxsaW5lIHgxPSIxOCIgeTE9IjYiIHgyPSI2IiB5Mj0iMTgiPjwvbGluZT48bGluZSB4MT0iNiIgeTE9IjYiIHgyPSIxOCIgeTI9IjE4Ij48L2xpbmU+PC9zdmc+");background-size:1.5rem}@media (min-width: 768px){[data-dark-mode] body .alert-dismissible .btn-close{background-size:1.25rem}}[data-dark-mode] .dropdown-item{color:#212529}[data-dark-mode] body hr.text-black-50{color:#6c757d !important}[data-dark-mode] body .email-form .form-control{background:#1b1f22;border:1px solid transparent}[data-dark-mode] body .email-form .form-control:focus{border:1px solid #8ed6fb}[data-dark-mode] .page-link{color:#8ed6fb;background-color:transparent;border:var(--bs-border-width) solid #343a40}[data-dark-mode] .page-link:hover{color:#212529;background-color:#dee2e6;border-color:#dee2e6}[data-dark-mode] .page-link:focus{color:#212529;background-color:#dee2e6}[data-dark-mode] .page-item.active .page-link{color:#212529;background-color:#8ed6fb;border-color:#8ed6fb}[data-dark-mode] .page-item.disabled .page-link{color:var(--bs-secondary-color);background-color:#1b1f22;border-color:#343a40}[data-dark-mode] .dropdown-menu{background:#1b1f22}[data-dark-mode] .dropdown-menu .dropdown-item{color:#dee2e6}[data-dark-mode] .dropdown-menu .dropdown-item:hover{color:#8ed6fb;background:#212529}[data-dark-mode] .dropdown-menu .dropdown-item.active,[data-dark-mode] .dropdown-menu .dropdown-item:focus{color:#8ed6fb;background:#212529}[data-dark-mode] .doks-navbar .dropdown-item.current,[data-dark-mode] .doks-subnavbar .dropdown-item.current{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23dee2e6' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1rem top 0.6rem;background-size:0.75rem 0.75rem}[data-dark-mode] details{border:1px solid #343a40}[data-dark-mode] summary:hover{background:#1b1f22}[data-dark-mode] details[open]>summary{border-bottom:1px solid #343a40}[data-dark-mode] details summary::before{content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28222, 226, 230, 0.75%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e")}[data-dark-mode] #toc a.active{color:#8ed6fb}[data-dark-mode] .btn-light{color:#8ed6fb;background:#1b1f22;border:1px solid #1b1f22}.alert{font-family:sfmono-regular,menlo,monaco,consolas,"Liberation Mono","Courier New",monospace;font-size:.875rem}.alert-icon{margin-right:0.75rem}.docs main .alert{margin:2rem -1.5rem}.alert .alert-link{text-decoration:underline}.alert-doks{background:#fbf7f0;color:#1d2d35}.alert-white{background-color:rgba(255,255,255,0.95)}.alert-primary{color:#fff;background-color:#5d2f86}.alert a{text-decoration:underline}.alert-primary .alert-link{color:#fff}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}.alert .alert-link:hover,.alert .alert-link:focus{text-decoration:none}.alert-dismissible .btn-close{position:absolute;top:50%;transform:translateY(-50%);right:1rem;z-index:2;padding:0.5rem;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-x'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");background-size:1.5rem;filter:invert(1) grayscale(100%) brightness(200%)}@media (min-width: 768px){.alert-dismissible .btn-close{background-size:1.25rem}}[data-global-alert="closed"] #announcement{display:none}.alert code{background:#f6ecdc;color:#1d2d35;padding:0.25rem 0.5rem}.navbar .btn-link{color:#1d2d35;padding:0.4375rem 0}#mode{padding:0.5rem}.btn-link:focus{outline:0;box-shadow:none}#navigation{margin-left:1.25rem}@media (min-width: 992px){#mode{margin-left:0.5rem;margin-right:0.25rem}.navbar .btn-link{padding:0.5625em 0.25rem 0.5rem 0.125rem}}.navbar .btn-link:hover{color:#5d2f86}.navbar .btn-link:active{color:#5d2f86}body .toggle-dark{display:block}body .toggle-light{display:none}[data-dark-mode] body .toggle-light{display:block}[data-dark-mode] body .toggle-dark{display:none}pre{position:relative}@media (max-width: 767.98px){.btn-copy{display:none}}.btn-copy{transition:opacity 0.3s ease-in-out;visibility:hidden !important;position:absolute;right:0.25rem;top:0.25rem;z-index:10;font-family:"Jost",-apple-system,blinkmacsystemfont,"Segoe UI",roboto,"Helvetica Neue",arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.875rem;padding:0.25rem 0.5rem;color:#fff;background-color:#d32e9d;border-color:#bf288e}.btn-copy:hover{color:#fff;background-color:#d743a7;border-color:#dc58b1}.btn-copy:focus{color:#fff;background-color:#d32e9d;border-color:#dc58b1;box-shadow:none}.btn-copy:active,.btn-copy.active{color:#fff;background-color:#d32e9d;border-color:#dc58b1}.btn-copy:active:focus,.btn-copy.active:focus{box-shadow:none}@media (min-width: 768px){pre:hover .btn-copy{visibility:visible !important}}.btn-copy::after{content:"Copy";display:block;color:#fff}.btn-copy:hover::after{content:"Copy";display:block;color:#fff}.btn-copy:focus::after,.btn-copy:active::after{content:"Copied";display:block;color:#fff}.collapsible-sidebar{margin:2.125rem 0}.btn-toggle{display:inline-flex;align-items:center;padding:0.25rem 0.5rem 0.25rem 0;font-weight:700;font-size:1rem;text-transform:uppercase;color:#1d2d35;background-color:transparent;border:0}.btn-toggle:hover,.btn-toggle:focus{color:#1d2d35;background-color:transparent;outline:0;box-shadow:none}.btn-toggle::before{width:1.25em;line-height:0;content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%2829, 45, 53, 0.75%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");transition:transform 0.35s ease;transform-origin:0.5em 50%;margin-bottom:0.125rem}.btn-toggle[aria-expanded="true"]{color:#1d2d35}.btn-toggle[aria-expanded="true"]::before{transform:rotate(90deg)}.btn-toggle-nav a{display:inline-flex;padding:0.1875rem 0.5rem;margin-top:0.125rem;margin-left:1.25rem;text-decoration:none}.btn-toggle-nav a:hover,.btn-toggle-nav a:focus{background-color:transparent;color:#5d2f86}.btn-toggle-nav a.active{color:#5d2f86}.dropdown-menu{width:auto}@media (min-width: 992px){.dropdown-menu{width:auto}}.doks-navbar .dropdown-menu,.doks-subnavbar .dropdown-menu{font-size:0.875rem}.doks-navbar .dropdown-item.current,.doks-subnavbar .dropdown-item.current{font-weight:600;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23292b2c' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right 1rem top 0.6rem;background-size:0.75rem 0.75rem}.btn-close{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-x'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");background-size:1.5rem}.offcanvas-header .btn-close{margin-right:0 !important}.dropdown-toggle::after{display:none}.dropdown-caret{margin-left:-0.1875rem;margin-right:-0.3125rem}.dropdown-menu-main .dropdown-item{color:inherit;font-size:1rem;font-weight:400;text-decoration:none}.dropdown-menu-main .dropdown-item:hover{background-color:transparent;color:#5d2f86}.dropdown-menu-main .dropdown-item.active{color:#5d2f86;font-weight:400;text-decoration:none;background-color:inherit}.dropdown-menu-main .dropdown-item.active:hover{background-color:transparent}pre,code,kbd,samp{font-family:sfmono-regular,menlo,monaco,consolas,"Liberation Mono","Courier New",monospace;font-size:.875rem;border-radius:.375rem}code{background:#fbf7f0;color:#1d2d35;padding:0.25rem 0.5rem}pre{margin:2rem 0}pre code{display:block;overflow-x:auto;line-height:1.5;padding:1.25rem 1.5rem;-moz-tab-size:4;-o-tab-size:4;tab-size:4;scrollbar-width:thin;scrollbar-color:transparent transparent}.hljs{padding:1.5rem !important}@media (max-width: 575.98px){pre,code,kbd,samp{border-radius:0}pre{margin:2rem -1.5rem}}pre code::-webkit-scrollbar{height:5px}pre code::-webkit-scrollbar-thumb{background:#ced4da}pre code:hover{scrollbar-width:thin;scrollbar-color:#adb5bd transparent}pre code::-webkit-scrollbar-thumb:hover{background:#adb5bd}code.language-mermaid{background:none}details{display:block;border:1px solid #e9ecef;border-radius:0.25rem;padding:0.5rem 1rem 0;margin:0.5rem 0}summary{list-style:none;display:inline-block;width:calc(100% + 2rem);margin:-0.5rem -1rem 0;padding:0.5rem 0.75rem}summary::-webkit-details-marker{display:none}summary:hover{background:#f8f9fa}details summary::before{display:inline-block;content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%2829, 45, 53, 0.75%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");transition:transform 0.35s ease;transform-origin:center center;margin-right:0.375rem}details[open]>summary::before{transform:rotate(90deg)}details[open]{padding:0.5rem 1rem}details[open]>summary{border-bottom:1px solid #dee2e6;margin-bottom:0.5rem}details h2,details .h2,details h3,details .h3,details h4,details .h4{margin:1rem 0 0.5rem}details p:last-child{margin-bottom:0}details ul,details ol{margin-bottom:0}details pre{margin:0 0 1rem}.hljs{display:block;overflow-x:auto;padding:1.25rem 1.5rem;background:#fbf7f0;color:#1d2d35}.hljs-string,.hljs-variable,.hljs-template-variable,.hljs-symbol,.hljs-bullet,.hljs-section,.hljs-addition,.hljs-attribute,.hljs-link{color:#d32e9d}.hljs-comment,.hljs-quote,.hljs-meta,.hljs-deletion{color:#888}.hljs-keyword,.hljs-selector-tag,.hljs-section,.hljs-name,.hljs-type,.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}[data-dark-mode] body .hljs{background:#1b1f22;color:#dee2e6}[data-dark-mode] body .hljs-string,[data-dark-mode] body .hljs-variable,[data-dark-mode] body .hljs-template-variable,[data-dark-mode] body .hljs-symbol,[data-dark-mode] body .hljs-bullet,[data-dark-mode] body .hljs-section,[data-dark-mode] body .hljs-addition,[data-dark-mode] body .hljs-attribute,[data-dark-mode] body .hljs-link{color:#8ed6fb}blockquote{margin-bottom:1rem;font-size:1.25rem;border-left:3px solid #dee2e6;padding-left:1rem}figure{margin:0 0 1rem;display:inline-block}figure img{margin-bottom:0.5rem;line-height:1;max-width:100%;height:auto}figure figcaption{margin:0.25rem 0 0.75rem;font-size:0.875em;color:#6c757d}.figure-caption{margin:0.25rem 0 0.75rem}.blur-up{filter:blur(5px)}.blur-up.lazyloaded{filter:unset}.mermaid{margin:1.5rem 0;padding:1.5rem}.mermaid svg{height:auto}.navbar-form{position:relative}#suggestions{position:absolute;right:0;margin-top:0.5rem;width:calc(100vw - 3rem);max-width:calc(400px - 3rem);z-index:1000}@media (min-width: 768px){#suggestions{right:-2rem}}@media (min-width: 992px){#suggestions{right:0}}#suggestions a,.suggestion__no-results{padding:0.75rem;margin:0 0.5rem}#suggestions a{display:block;text-decoration:none}#suggestions a:focus{background:#f8f9fa;outline:0}#suggestions div:not(:first-child){border-top:1px dashed #e9ecef}#suggestions div:first-child{margin-top:0.5rem}#suggestions div:last-child{margin-bottom:0.5rem}#suggestions a:hover{background:#f8f9fa}#suggestions span{display:flex;font-size:1rem}.suggestion__title{font-weight:700;color:#1d2d35}.suggestion__description,.suggestion__no-results{color:#495057}@media (min-width: 992px){#suggestions{width:31.125rem;max-width:31.125rem}#suggestions a{display:flex}.suggestion__title{width:9rem;padding-right:1rem;border-right:1px solid #e9ecef;display:inline-block;text-align:right}.suggestion__description{width:19rem;padding-left:1rem}}table{margin:3rem 0}.footer{border-top:1px solid #e9ecef;padding-top:1.125rem;padding-bottom:1.125rem}.footer ul{margin-bottom:0}.footer li{font-size:.875rem;margin-bottom:0}@media (min-width: 768px){.footer li{font-size:1rem}}.navbar-text{margin-left:1rem}.navbar-brand{font-weight:700}.navbar{z-index:1000;background-color:rgba(255,255,255,0.95);border-bottom:1px solid #e9ecef}@media (min-width: 992px){.navbar{z-index:1025;padding-top:0.25rem;padding-bottom:0.25rem}}@media (min-width: 768px){.navbar-brand{font-size:1.375rem}.navbar-text{margin-left:1.25rem}}.navbar-nav{flex-direction:row}.nav-item{margin-left:0}@media (min-width: 768px){.nav-item{margin-left:0.5rem}}@media (max-width: 767.98px){.navbar .container{padding-left:1.5rem;padding-right:1.5rem}}button#doks-languages{margin:0.25rem 0 0}@media (min-width: 992px){button#doks-languages{margin:0.25rem 0.5rem 0 0.25rem}}button#doks-versions{margin:0.25rem 0 0}@media (min-width: 992px){button#doks-versions{margin:0.25rem 0.5rem 0 0.25rem}}.offcanvas .nav-link{color:#1d2d35}.doks-subnavbar{background-color:rgba(255,255,255,0.95);border-bottom:1px solid #e9ecef}.doks-subnavbar .nav-link{padding:0.5rem 1.5rem 0.5rem 0}.doks-subnavbar .nav-link:first-child{padding:0.5rem 1.5rem 0.5rem 0}.offcanvas .nav-link:hover,.offcanvas .nav-link:focus{color:#5d2f86}.offcanvas .nav-link.active{color:#5d2f86}.header-bar{border-top:4px solid;border-image-source:linear-gradient(90deg, #5d2f86, #8ed6fb 50%, #d32e9d);border-image-slice:1}.offcanvas .header-bar{margin-bottom:-4px}@media (min-width: 768px){.navbar-brand{margin-right:0.75rem !important}.doks-search{max-width:20rem;margin-top:0.125rem;margin-bottom:0.125rem}}.form-control.is-search{padding-right:4rem;border:1px solid transparent;background:#f8f9fa}@media (min-width: 768px){.form-control.is-search{width:calc(100% + 2rem)}}@media (min-width: 992px){.form-control.is-search{width:100%}}.form-control.is-search:focus{border:1px solid #5d2f86}.doks-search::after{position:absolute;top:0.4625rem;right:0.5375rem;display:flex;align-items:center;justify-content:center;height:1.5rem;padding-right:0.3125rem;padding-left:0.3125rem;font-size:.75rem;color:#495057;content:"Ctrl + /";border:1px solid #dee2e6;border-radius:0.25rem}@media (min-width: 768px){.doks-search::after{right:-1.4625rem}}@media (min-width: 992px){.doks-search::after{right:0.3125rem}}.algolia-autocomplete{display:flex !important}.algolia-autocomplete .ds-dropdown-menu{box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}@media (max-width: 575.98px){.algolia-autocomplete .ds-dropdown-menu{max-width:512px !important;min-width:312px !important;width:auto !important}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{font-weight:normal}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column::after{content:"/";margin-right:0.25rem}}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{color:#1d2d35}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:0}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{padding:0 0.05em}.algolia-autocomplete .algolia-docsearch-footer{margin-top:1rem;margin-right:0.5rem;margin-bottom:0.5rem}.navbar .menu-icon{cursor:pointer;padding:1.125rem 0.625rem;margin:0 0 0 -0.625rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.navbar .menu-icon .navicon{background:#1d2d35;display:block;height:2px;position:relative;transition:background 0.2s ease-out;width:18px}.navbar .menu-icon .navicon::before,.navbar .menu-icon .navicon::after{background:#1d2d35;content:"";display:block;height:100%;position:absolute;transition:all 0.2s ease-out;width:100%}.navbar .menu-icon .navicon::before{top:5px}.navbar .menu-icon .navicon::after{top:-5px}.btn-menu{margin-left:1rem;border:transparent}.btn-doks-light{border:transparent}.btn-menu,.doks-sidebar-toggle{padding-right:0.25rem;padding-left:0.25rem;margin-right:-0.5rem}.btn-menu:hover,.btn-doks-light:hover,.doks-sidebar-toggle:hover{background:#fcfaff;border:transparent}.btn-menu:focus,.btn-doks-light:focus,.doks-sidebar-toggle:focus{outline:0;border:transparent}.doks-sidebar-toggle .doks-collapse,.doks-toc-toggle .doks-collapse{display:none}.doks-sidebar-toggle:not(.collapsed) .doks-expand,.doks-toc-toggle:not(.collapsed) .doks-expand{display:none}.doks-sidebar-toggle:not(.collapsed) .doks-collapse,.doks-toc-toggle:not(.collapsed) .doks-collapse{display:inline-block}.navbar-light .navbar-brand,.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:active{color:#1d2d35}.navbar-light .navbar-nav .active .nav-link{color:#5d2f86}.dropdown-divider{border-top:1px dashed #e9ecef}.dropdown-item:hover{background:#f8f9fa}.dropdown-item:active{color:inherit}.social-link{padding-left:0.5rem}@media (min-width: 768px){.social-link{padding-left:0}}@media (min-width: 992px){.social-link{padding-right:0.5rem;padding-left:0.5rem}}.docs-content>h2[id]::before,.docs-content>[id].h2::before,.docs-content>h3[id]::before,.docs-content>[id].h3::before,.docs-content>h4[id]::before,.docs-content>[id].h4::before{display:block;height:6rem;margin-top:-6rem;content:""}.anchor{visibility:hidden}h1:hover a,.h1:hover a,h2:hover a,.h2:hover a,h3:hover a,.h3:hover a,h4:hover a,.h4:hover a{visibility:visible;text-decoration:none}.card-list{margin-top:2.25rem}.page-footer-meta{margin-top:3rem}.edit-page,.last-modified{font-size:.875rem;margin-top:0.25rem;margin-bottom:0.25rem}@media (min-width: 768px){.edit-page,.last-modified{font-size:1rem;margin-top:0.75rem;margin-bottom:0.25rem}}.edit-page svg,.last-modified svg{margin-right:0.25rem;margin-bottom:0.25rem}p.meta{margin-top:0.5rem;font-size:1rem}.breadcrumb{margin-top:2.25rem;font-size:1rem}.page-link:hover{text-decoration:none}.ratio-new{width:640px;height:264px;position:relative;margin:auto}.home .card,.contributors.list .card,.blog.list .card,.blog.single .card,.categories.list .card,.tags.list .card{margin-top:2rem;margin-bottom:2rem;transition:transform 0.3s}.home .card:hover,.contributors.list .card:hover,.blog.list .card:hover,.blog.single .card:hover,.categories.list .card:hover,.tags.list .card:hover{transform:scale(1.025)}.contributors.list .card.card-terms:hover,.categories.list .card.card-terms:hover,.tags.list .card.card-terms:hover{transform:none}.home .card-body,.contributors.list .card-body,.blog.list .card-body,.blog.single .card-body,.categories.list .card-body,.tags.list .card-body{padding:0 2rem 1rem}.contributors.list .card-terms .card-body,.categories.list .card-terms .card-body,.tags.list .card-terms .card-body{padding:1rem}.blog-header{text-align:center;margin-bottom:2rem}.related-posts{margin-top:4rem}h2.section-title,.section-title.h2{margin-bottom:1.25rem}.docs-links,.docs-toc{scrollbar-width:thin;scrollbar-color:#fff #fff}.docs-links::-webkit-scrollbar,.docs-toc::-webkit-scrollbar{width:5px}.docs-links::-webkit-scrollbar-track,.docs-toc::-webkit-scrollbar-track{background:#fff}.docs-links::-webkit-scrollbar-thumb,.docs-toc::-webkit-scrollbar-thumb{background:#fff}.docs-links:hover,.docs-toc:hover{scrollbar-width:thin;scrollbar-color:#e9ecef #fff}.docs-links:hover::-webkit-scrollbar-thumb,.docs-toc:hover::-webkit-scrollbar-thumb{background:#e9ecef}.docs-links::-webkit-scrollbar-thumb:hover,.docs-toc::-webkit-scrollbar-thumb:hover{background:#e9ecef}.docs-links h3,.docs-links .h3,.page-links h3,.page-links .h3{text-transform:uppercase;font-size:1rem;margin:1.25rem 0 0.5rem;padding:1.5rem 0 0}@media (min-width: 992px){.docs-links h3,.docs-links .h3,.page-links h3,.page-links .h3{margin:1.125rem 1.5rem 0.75rem 0;padding:1.375rem 0 0}}.docs-links h3:not(:first-child),.docs-links .h3:not(:first-child){border-top:1px solid #e9ecef}a.docs-link{color:#1d2d35;display:block;padding:0.125rem 0;font-size:1rem}.page-links li{margin-top:0.375rem;padding-top:0.375rem}.page-links li ul li{border-top:none;padding-left:1rem;margin-top:0.125rem;padding-top:0.125rem}.page-links li:not(:first-child){border-top:1px dashed #e9ecef}.page-links a{color:#1d2d35;display:block;padding:0.125rem 0;font-size:.9375rem}.docs-link:hover,.docs-link.active,.page-links a:hover{text-decoration:none;color:#5d2f86}.nav-link.active,.dropdown-menu-main .dropdown-item.active,.docs-link.active{font-weight:500}.docs-links h3.sidebar-link,.docs-links .sidebar-link.h3,.page-links h3.sidebar-link,.page-links .sidebar-link.h3{text-transform:none;font-size:1.125rem;font-weight:normal}.docs-links h3.sidebar-link a,.docs-links .sidebar-link.h3 a,.page-links h3.sidebar-link a,.page-links .sidebar-link.h3 a{color:#1d2d35}.docs-links h3.sidebar-link a:hover,.docs-links .sidebar-link.h3 a:hover,.page-links h3.sidebar-link a:hover,.page-links .sidebar-link.h3 a:hover{text-decoration:underline} diff --git a/theme/public/png/gala-arch.png b/theme/public/png/gala-arch.png new file mode 100644 index 0000000000000000000000000000000000000000..7f71718008dce4bd2c358a2af19a0b472ff04e40 Binary files /dev/null and b/theme/public/png/gala-arch.png differ diff --git a/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_2f4405fc4e5d8807614d40c3e3d80405.png b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_2f4405fc4e5d8807614d40c3e3d80405.png new file mode 100644 index 0000000000000000000000000000000000000000..37ea213a729497ee532a7dfacb1df4c02eae0e70 Binary files /dev/null and b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_2f4405fc4e5d8807614d40c3e3d80405.png differ diff --git a/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_900x0_resize_box_3.png b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_900x0_resize_box_3.png new file mode 100644 index 0000000000000000000000000000000000000000..380b5b8364151ce73fc2eab768b498fd2dfd1203 Binary files /dev/null and b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_900x0_resize_box_3.png differ diff --git a/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_a636998afbbb31154975bc44864d2540.png b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_a636998afbbb31154975bc44864d2540.png new file mode 100644 index 0000000000000000000000000000000000000000..36fa09e3a2d0e78ebbeeebd0d7bae541355fc2ec Binary files /dev/null and b/theme/public/png/gala-arch_hua06946604bdcdad5331e15234f85f16b_163804_a636998afbbb31154975bc44864d2540.png differ diff --git a/theme/public/png/partner.png b/theme/public/png/partner.png new file mode 100644 index 0000000000000000000000000000000000000000..28a1ec5c7254309917807e88eaf6a6a7980390e9 Binary files /dev/null and b/theme/public/png/partner.png differ diff --git a/theme/public/png/partner_hu039094132afdc8c1aeb19a42974807fb_18134_449x0_resize_box_3.png b/theme/public/png/partner_hu039094132afdc8c1aeb19a42974807fb_18134_449x0_resize_box_3.png new file mode 100644 index 0000000000000000000000000000000000000000..9094219a2d7cf5ebcd6884a69789ecc520bc8195 Binary files /dev/null and b/theme/public/png/partner_hu039094132afdc8c1aeb19a42974807fb_18134_449x0_resize_box_3.png differ diff --git a/theme/public/tags/index.html b/theme/public/tags/index.html index 60e8581b8d0fdff1216c93d1b8fe96a0a71b2716..6a5a57ed7c711cca215f936ae2e4c1354737c6b6 100644 --- a/theme/public/tags/index.html +++ b/theme/public/tags/index.html @@ -10,7 +10,7 @@ - + @@ -240,10 +240,10 @@ - + - +