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  }