Esempio n. 1
0
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
}
Esempio n. 2
0
// @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
}
Esempio n. 3
0
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
		}
	}
}