github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/cmd/startup/startup.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package startup 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 htmltemplate "html/template" 24 "net" 25 "os" 26 "os/signal" 27 "syscall" 28 texttemplate "text/template" 29 "time" 30 31 "github.com/siglens/siglens/pkg/alerts/alertsHandler" 32 "github.com/siglens/siglens/pkg/blob" 33 local "github.com/siglens/siglens/pkg/blob/local" 34 "github.com/siglens/siglens/pkg/config" 35 commonconfig "github.com/siglens/siglens/pkg/config/common" 36 "github.com/siglens/siglens/pkg/dashboards" 37 "github.com/siglens/siglens/pkg/hooks" 38 "github.com/siglens/siglens/pkg/instrumentation" 39 "github.com/siglens/siglens/pkg/localnodeid" 40 "github.com/siglens/siglens/pkg/querytracker" 41 "github.com/siglens/siglens/pkg/retention" 42 "github.com/siglens/siglens/pkg/scroll" 43 "github.com/siglens/siglens/pkg/segment/memory/limit" 44 tracinghandler "github.com/siglens/siglens/pkg/segment/tracing/handler" 45 "github.com/siglens/siglens/pkg/segment/writer" 46 "github.com/siglens/siglens/pkg/segment/writer/metrics" 47 ingestserver "github.com/siglens/siglens/pkg/server/ingest" 48 queryserver "github.com/siglens/siglens/pkg/server/query" 49 "github.com/siglens/siglens/pkg/ssa" 50 "github.com/siglens/siglens/pkg/usageStats" 51 usq "github.com/siglens/siglens/pkg/usersavedqueries" 52 "github.com/siglens/siglens/pkg/utils" 53 vtable "github.com/siglens/siglens/pkg/virtualtable" 54 log "github.com/sirupsen/logrus" 55 "gopkg.in/natefinch/lumberjack.v2" 56 ) 57 58 var StdOutLogger *log.Logger 59 60 func init() { 61 StdOutLogger = &log.Logger{ 62 Out: os.Stderr, 63 Formatter: new(log.TextFormatter), 64 Hooks: make(log.LevelHooks), 65 Level: log.InfoLevel, 66 } 67 customFormatter := new(log.TextFormatter) 68 customFormatter.TimestampFormat = "2006-01-02 15:04:05" 69 customFormatter.FullTimestamp = true 70 StdOutLogger.SetFormatter(customFormatter) 71 } 72 73 func initlogger() { 74 customFormatter := new(log.TextFormatter) 75 customFormatter.TimestampFormat = "2006-01-02 15:04:05" 76 log.SetFormatter(customFormatter) 77 customFormatter.FullTimestamp = true 78 } 79 80 func Main() { 81 if hook := hooks.GlobalHooks.StartupHook; hook != nil { 82 hook() 83 } 84 85 initlogger() 86 utils.SetServerStartTime(time.Now()) 87 err := config.InitConfigurationData() 88 if err != nil { 89 log.Error("Failed to initialize config! Exiting to avoid misconfigured server...") 90 os.Exit(1) 91 } 92 93 validateDeploymentHook := hooks.GlobalHooks.ValidateDeploymentHook 94 if validateDeploymentHook == nil { 95 validateDeploymentHook = config.ValidateDeployment 96 } 97 98 nodeType, err := validateDeploymentHook() 99 if err != nil { 100 log.Errorf("Invalid deployment type! Error=[%+v]", err) 101 os.Exit(1) 102 } 103 104 getNodeIdHook := hooks.GlobalHooks.GetNodeIdHook 105 if getNodeIdHook == nil { 106 getNodeIdHook = localnodeid.GetRunningNodeID 107 } 108 109 nodeID := getNodeIdHook() 110 err = config.InitDerivedConfig(nodeID) 111 if err != nil { 112 log.Errorf("Error initializing derived configurations! %v", err) 113 os.Exit(1) 114 } 115 116 serverCfg := *config.GetRunningConfig() // Init the Configuration 117 var logOut string 118 if config.GetLogPrefix() == "" { 119 logOut = "stdout" 120 } else { 121 logOut = serverCfg.Log.LogPrefix + "siglens.log" 122 } 123 baseLogDir := serverCfg.Log.LogPrefix 124 if baseLogDir == "" { 125 log.SetOutput(os.Stdout) 126 } else { 127 err := os.MkdirAll(baseLogDir, 0764) 128 if err != nil { 129 log.Fatalf("failed to make log directory at=%v, err=%v", baseLogDir, err) 130 } 131 log.SetOutput(&lumberjack.Logger{ 132 Filename: logOut, 133 MaxSize: serverCfg.Log.LogFileRotationSizeMB, 134 MaxBackups: 30, 135 MaxAge: 1, //days 136 Compress: serverCfg.Log.CompressLogFile, 137 }) 138 } 139 if config.IsDebugMode() { 140 log.SetLevel(log.DebugLevel) 141 } else { 142 log.SetLevel(log.InfoLevel) 143 } 144 log.Infof("----- Siglens server type %s starting up.... ----- \n", nodeType.String()) 145 log.Infof("----- Siglens server logging to %s ----- \n", logOut) 146 147 if hook := hooks.GlobalHooks.CheckLicenseHook; hook != nil { 148 hook() 149 } 150 151 if hook := hooks.GlobalHooks.LogConfigHook; hook != nil { 152 hook() 153 } else { 154 configJSON, err := json.MarshalIndent(serverCfg, "", " ") 155 if err != nil { 156 log.Errorf("main : Error marshalling config struct %v", err.Error()) 157 } 158 log.Infof("Running config %s", string(configJSON)) 159 } 160 161 if hook := hooks.GlobalHooks.AfterConfigHook; hook != nil { 162 hook(baseLogDir) 163 } 164 165 err = StartSiglensServer(nodeType, nodeID) 166 if err != nil { 167 ShutdownSiglensServer() 168 if baseLogDir != "" { 169 StdOutLogger.Errorf("siglens main: Error in starting server:%v ", err) 170 } 171 log.Errorf("siglens main: Error in starting server:%v ", err) 172 os.Exit(1) 173 } 174 if hook := hooks.GlobalHooks.CheckOrgValidityHook; hook != nil { 175 hook() 176 } 177 ch := make(chan os.Signal, 1) 178 signal.Notify(ch, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 179 180 switch <-ch { 181 case os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT: 182 log.Errorf("Interrupt signal received. Exiting server...") 183 ShutdownSiglensServer() 184 log.Errorf("Server shutdown") 185 os.Exit(0) 186 default: 187 log.Errorf("Something went wrong. Exiting server...") 188 ShutdownSiglensServer() 189 log.Errorf("Server shutdown") 190 os.Exit(1) 191 } 192 } 193 194 // Licenses should be checked outside of this function 195 func StartSiglensServer(nodeType commonconfig.DeploymentType, nodeID string) error { 196 if nodeID == "" { 197 return fmt.Errorf("nodeID cannot be empty") 198 } 199 200 if hook := hooks.GlobalHooks.StartSiglensExtrasHook; hook != nil { 201 err := hook(nodeID) 202 if err != nil { 203 return err 204 } 205 } 206 207 err := alertsHandler.ConnectSiglensDB() 208 if err != nil { 209 log.Errorf("Failed to connect to siglens database, err: %v", err) 210 fmt.Printf("Failed to connect to siglens database, err: %v\n", err) 211 return err 212 } 213 214 limit.InitMemoryLimiter() 215 216 usageStats.StartUsageStats() 217 ingestNode := config.IsIngestNode() 218 queryNode := config.IsQueryNode() 219 ingestServer := fmt.Sprint(config.GetIngestListenIP()) + ":" + fmt.Sprintf("%d", config.GetIngestPort()) 220 queryServer := fmt.Sprint(config.GetQueryListenIP()) + ":" + fmt.Sprintf("%d", config.GetQueryPort()) 221 222 if config.IsTlsEnabled() && (config.GetTLSCertificatePath() == "" || config.GetTLSPrivateKeyPath() == "") { 223 fmt.Println("TLS is enabled but certificate or private key path is not provided") 224 log.Fatalf("TLS is enabled but certificate or private key path is not provided") 225 } 226 227 err = vtable.InitVTable() 228 if err != nil { 229 log.Fatalf("error in InitVTable: %v", err) 230 } 231 232 log.Infof("StartSiglensServer: Initialilizing Blob Store") 233 err = blob.InitBlobStore() 234 if err != nil { 235 log.Errorf("StartSiglensServer: Error initializing S3: %v", err) 236 return err 237 } 238 239 ssa.InitSsa() 240 241 err = usq.InitUsq() 242 if err != nil { 243 log.Errorf("error in init UserSavedQueries: %v", err) 244 return err 245 } 246 err = retention.InitRetentionCleaner() 247 if err != nil { 248 log.Errorf("error in init retention cleaner: %v", err) 249 return err 250 } 251 err = dashboards.InitDashboards() 252 if err != nil { 253 log.Errorf("error in init Dashboards: %v", err) 254 return err 255 } 256 257 siglensStartupLog := fmt.Sprintf("----- Siglens server type %s starting up ----- \n", nodeType) 258 if config.GetLogPrefix() != "" { 259 StdOutLogger.Infof(siglensStartupLog) 260 } 261 log.Infof(siglensStartupLog) 262 if queryNode { 263 err := usq.InitUsq() 264 if err != nil { 265 log.Errorf("error in init UserSavedQueries: %v", err) 266 return err 267 } 268 269 err = dashboards.InitDashboards() 270 if err != nil { 271 log.Errorf("error in init Dashboards: %v", err) 272 return err 273 } 274 } 275 276 if ingestNode { 277 startIngestServer(ingestServer) 278 } 279 if queryNode { 280 startQueryServer(queryServer) 281 } 282 283 instrumentation.InitMetrics() 284 querytracker.InitQT() 285 286 go tracinghandler.MonitorSpansHealth() 287 go tracinghandler.DependencyGraphThread() 288 289 return nil 290 } 291 292 func ShutdownSiglensServer() { 293 // force write unsaved data to segfile and flush bloom, range, updates to meta 294 writer.ForcedFlushToSegfile() 295 metrics.ForceFlushMetricsBlock() 296 err := vtable.FlushAliasMapToFile() 297 if err != nil { 298 log.Errorf("flushing of aliasmap file failed, err=%v", err) 299 } 300 local.ForceFlushSegSetKeysToFile() 301 scroll.ForcedFlushToScrollFile() 302 ssa.StopSsa() 303 usageStats.ForceFlushStatstoFile() 304 alertsHandler.Disconnect() 305 } 306 307 func startIngestServer(serverAddr string) { 308 siglensStartupLog := fmt.Sprintf("----- Siglens Ingestion server starting on %s ----- \n", serverAddr) 309 if config.GetLogPrefix() != "" { 310 StdOutLogger.Infof(siglensStartupLog) 311 } 312 log.Infof(siglensStartupLog) 313 cfg := config.DefaultIngestionHttpConfig() 314 s := ingestserver.ConstructIngestServer(cfg, serverAddr) 315 go func() { 316 var err error 317 if config.IsSafeMode() { 318 err = s.RunSafeServer() 319 } else { 320 err = s.Run() 321 } 322 if err != nil { 323 var opErr *net.OpError 324 if errors.As(err, &opErr) { 325 if opErr.Op == "listen" { 326 StdOutLogger.Errorf("Failed to start server: %v", err) 327 os.Exit(1) 328 } 329 } 330 } 331 }() 332 } 333 334 func startQueryServer(serverAddr string) { 335 siglensStartupLog := fmt.Sprintf("----- Siglens Query server starting on %s ----- \n", serverAddr) 336 siglensUIStartupLog := fmt.Sprintf("----- Siglens UI starting on %s ----- \n", serverAddr) 337 if config.GetLogPrefix() != "" { 338 StdOutLogger.Infof(siglensStartupLog) 339 StdOutLogger.Infof(siglensUIStartupLog) 340 } 341 log.Infof(siglensStartupLog) 342 log.Infof(siglensUIStartupLog) 343 cfg := config.DefaultQueryServerHttpConfig() 344 s := queryserver.ConstructQueryServer(cfg, serverAddr) 345 go func() { 346 var err error 347 if config.IsSafeMode() { 348 err = s.RunSafeServer() 349 } else { 350 htmlTemplate := htmltemplate.New("html").Funcs(htmltemplate.FuncMap{ 351 "safeHTML": func(htmlContent string) htmltemplate.HTML { 352 return htmltemplate.HTML(htmlContent) 353 }, 354 }) 355 textTemplate := texttemplate.New("other") 356 357 parseTemplatesHook := hooks.GlobalHooks.ParseTemplatesHook 358 if parseTemplatesHook == nil { 359 log.Fatalf("startQueryServer: ParseTemplatesHook is nil") 360 } 361 parseTemplatesHook(htmlTemplate, textTemplate) 362 363 err = s.Run(htmlTemplate, textTemplate) 364 } 365 if err != nil { 366 var opErr *net.OpError 367 if errors.As(err, &opErr) { 368 if opErr.Op == "listen" { 369 StdOutLogger.Errorf("Failed to start server: %v", err) 370 os.Exit(1) 371 } 372 } 373 } 374 }() 375 }