func MakeReport(config Config, interval *Interval, result *Result) *Report { // Sort classes by Query_time_sum, descending. sort.Sort(ByQueryTime(result.Class)) // Make Report from Result and other metadata (e.g. Interval). report := &Report{ ServiceInstance: config.ServiceInstance, StartTs: interval.StartTime, EndTs: interval.StopTime, RunTime: result.RunTime, Global: result.Global, Class: result.Class, } if interval != nil { size, err := pct.FileSize(interval.Filename) if err != nil { size = 0 } // slow log data report.SlowLogFile = interval.Filename report.SlowLogFileSize = size report.StartOffset = interval.StartOffset report.EndOffset = interval.EndOffset report.StopOffset = result.StopOffset } // Return all query classes if there's no limit or number of classes is // less than the limit. n := len(result.Class) if config.ReportLimit == 0 || n <= int(config.ReportLimit) { return report // all classes, no LRQ } // Top queries report.Class = result.Class[0:config.ReportLimit] // Low-ranking Queries lrq := event.NewQueryClass("0", "", false, 0*time.Second) for _, query := range result.Class[config.ReportLimit:n] { addQuery(lrq, query) } report.Class = append(report.Class, lrq) return report // top classes, the rest as LRQ }
// @goroutine[1] func (m *Manager) rotateSlowLog(config Config, interval *Interval) error { m.logger.Debug("rotateSlowLog:call") defer m.logger.Debug("rotateSlowLog:return") m.status.Update("qan-log-parser", "Rotating slow log") if err := m.mysqlConn.Connect(2); err != nil { m.logger.Warn(err) return err } defer m.mysqlConn.Close() // Stop slow log so we don't move it while MySQL is using it. if err := m.mysqlConn.Set(config.Stop); err != nil { return err } // Move current slow log by renaming it. newSlowLogFile := fmt.Sprintf("%s-%d", interval.Filename, time.Now().UTC().Unix()) if err := os.Rename(interval.Filename, newSlowLogFile); err != nil { return err } // Re-enable slow log. if err := m.mysqlConn.Set(config.Start); err != nil { return err } // Modify interval so worker parses the rest of the old slow log. interval.Filename = newSlowLogFile interval.EndOffset, _ = pct.FileSize(newSlowLogFile) // todo: handle err // Save old slow log and remove later if configured to do so. if config.RemoveOldSlowLogs { m.workersMux.RLock() m.oldSlowLogs[newSlowLogFile] = len(m.workers) + 1 m.workersMux.RUnlock() } return nil }
func (i *FileIntervalIter) run() { defer func() { i.running = false i.sync.Done() }() var prevFileInfo os.FileInfo cur := &Interval{} for { i.logger.Debug("run:wait") select { case now := <-i.tickChan: i.logger.Debug("run:tick") // Get the MySQL slow log file name at each interval because it can change. curFile, err := i.filename() if err != nil { i.logger.Warn(err) cur = new(Interval) continue } // Get the current size of the MySQL slow log. i.logger.Debug("run:file size") curSize, err := pct.FileSize(curFile) if err != nil { i.logger.Warn(err) cur = new(Interval) continue } i.logger.Debug(fmt.Sprintf("run:%s:%d", curFile, curSize)) // File changed if prev file not same as current file. // @todo: Normally this only changes when QAN manager rotates slow log // at interval. If it changes for another reason (e.g. user // renames slow log) then StartOffset=0 may not be ideal. curFileInfo, _ := os.Stat(curFile) fileChanged := !os.SameFile(prevFileInfo, curFileInfo) prevFileInfo = curFileInfo if !cur.StartTime.IsZero() { // StartTime is set i.logger.Debug("run:next") i.intervalNo++ // End of current interval: cur.Filename = curFile if fileChanged { // Start from beginning of new file. i.logger.Info("File changed") cur.StartOffset = 0 } cur.EndOffset = curSize cur.StopTime = now cur.Number = i.intervalNo // Send interval to manager which should be ready to receive it. select { case i.intervalChan <- cur: case <-time.After(1 * time.Second): i.logger.Warn(fmt.Sprintf("Lost interval: %+v", cur)) } // Next interval: cur = &Interval{ StartTime: now, StartOffset: curSize, } } else { // First interval, either due to first tick or because an error // occurred earlier so a new interval was started. i.logger.Debug("run:first") cur.StartOffset = curSize cur.StartTime = now prevFileInfo, _ = os.Stat(curFile) } case <-i.sync.StopChan: i.logger.Debug("run:stop") return } } }