You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							258 lines
						
					
					
						
							7.3 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							258 lines
						
					
					
						
							7.3 KiB
						
					
					
				
								package tasks
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"fmt"
							 | 
						|
									"os"
							 | 
						|
									"path/filepath"
							 | 
						|
									"strings"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/worker_pb"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// TaskLogHandler handles task log requests from admin server
							 | 
						|
								type TaskLogHandler struct {
							 | 
						|
									baseLogDir string
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// NewTaskLogHandler creates a new task log handler
							 | 
						|
								func NewTaskLogHandler(baseLogDir string) *TaskLogHandler {
							 | 
						|
									if baseLogDir == "" {
							 | 
						|
										baseLogDir = "/tmp/seaweedfs/task_logs"
							 | 
						|
									}
							 | 
						|
									// Best-effort ensure the base directory exists so reads don't fail due to missing dir
							 | 
						|
									if err := os.MkdirAll(baseLogDir, 0755); err != nil {
							 | 
						|
										glog.Warningf("Failed to create base task log directory %s: %v", baseLogDir, err)
							 | 
						|
									}
							 | 
						|
									return &TaskLogHandler{
							 | 
						|
										baseLogDir: baseLogDir,
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// HandleLogRequest processes a task log request and returns the response
							 | 
						|
								func (h *TaskLogHandler) HandleLogRequest(request *worker_pb.TaskLogRequest) *worker_pb.TaskLogResponse {
							 | 
						|
									response := &worker_pb.TaskLogResponse{
							 | 
						|
										TaskId:   request.TaskId,
							 | 
						|
										WorkerId: request.WorkerId,
							 | 
						|
										Success:  false,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Find the task log directory
							 | 
						|
									logDir, err := h.findTaskLogDirectory(request.TaskId)
							 | 
						|
									if err != nil {
							 | 
						|
										response.ErrorMessage = fmt.Sprintf("Task log directory not found: %v", err)
							 | 
						|
										glog.Warningf("Task log request failed for %s: %v", request.TaskId, err)
							 | 
						|
								
							 | 
						|
										// Add diagnostic information to help debug the issue
							 | 
						|
										response.LogEntries = []*worker_pb.TaskLogEntry{
							 | 
						|
											{
							 | 
						|
												Timestamp: time.Now().Unix(),
							 | 
						|
												Level:     "WARNING",
							 | 
						|
												Message:   fmt.Sprintf("Task logs not available: %v", err),
							 | 
						|
												Fields:    map[string]string{"source": "task_log_handler"},
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Timestamp: time.Now().Unix(),
							 | 
						|
												Level:     "INFO",
							 | 
						|
												Message:   fmt.Sprintf("This usually means the task was never executed on this worker or logs were cleaned up. Base log directory: %s", h.baseLogDir),
							 | 
						|
												Fields:    map[string]string{"source": "task_log_handler"},
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										// response.Success remains false to indicate logs were not found
							 | 
						|
										return response
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Read metadata if requested
							 | 
						|
									if request.IncludeMetadata {
							 | 
						|
										metadata, err := h.readTaskMetadata(logDir)
							 | 
						|
										if err != nil {
							 | 
						|
											response.ErrorMessage = fmt.Sprintf("Failed to read task metadata: %v", err)
							 | 
						|
											glog.Warningf("Failed to read metadata for task %s: %v", request.TaskId, err)
							 | 
						|
											return response
							 | 
						|
										}
							 | 
						|
										response.Metadata = metadata
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Read log entries
							 | 
						|
									logEntries, err := h.readTaskLogEntries(logDir, request)
							 | 
						|
									if err != nil {
							 | 
						|
										response.ErrorMessage = fmt.Sprintf("Failed to read task logs: %v", err)
							 | 
						|
										glog.Warningf("Failed to read logs for task %s: %v", request.TaskId, err)
							 | 
						|
										return response
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									response.LogEntries = logEntries
							 | 
						|
									response.Success = true
							 | 
						|
								
							 | 
						|
									glog.V(1).Infof("Successfully retrieved %d log entries for task %s", len(logEntries), request.TaskId)
							 | 
						|
									return response
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// findTaskLogDirectory searches for the task log directory by task ID
							 | 
						|
								func (h *TaskLogHandler) findTaskLogDirectory(taskID string) (string, error) {
							 | 
						|
									entries, err := os.ReadDir(h.baseLogDir)
							 | 
						|
									if err != nil {
							 | 
						|
										return "", fmt.Errorf("failed to read base log directory %s: %w", h.baseLogDir, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Look for directories that start with the task ID
							 | 
						|
									var candidateDirs []string
							 | 
						|
									for _, entry := range entries {
							 | 
						|
										if entry.IsDir() {
							 | 
						|
											candidateDirs = append(candidateDirs, entry.Name())
							 | 
						|
											if strings.HasPrefix(entry.Name(), taskID+"_") {
							 | 
						|
												return filepath.Join(h.baseLogDir, entry.Name()), nil
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Enhanced error message with diagnostic information
							 | 
						|
									return "", fmt.Errorf("task log directory not found for task ID: %s (searched %d directories in %s, directories found: %v)",
							 | 
						|
										taskID, len(candidateDirs), h.baseLogDir, candidateDirs)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// readTaskMetadata reads task metadata from the log directory
							 | 
						|
								func (h *TaskLogHandler) readTaskMetadata(logDir string) (*worker_pb.TaskLogMetadata, error) {
							 | 
						|
									metadata, err := GetTaskLogMetadata(logDir)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Convert to protobuf metadata
							 | 
						|
									pbMetadata := &worker_pb.TaskLogMetadata{
							 | 
						|
										TaskId:      metadata.TaskID,
							 | 
						|
										TaskType:    metadata.TaskType,
							 | 
						|
										WorkerId:    metadata.WorkerID,
							 | 
						|
										StartTime:   metadata.StartTime.Unix(),
							 | 
						|
										Status:      metadata.Status,
							 | 
						|
										Progress:    float32(metadata.Progress),
							 | 
						|
										VolumeId:    metadata.VolumeID,
							 | 
						|
										Server:      metadata.Server,
							 | 
						|
										Collection:  metadata.Collection,
							 | 
						|
										LogFilePath: metadata.LogFilePath,
							 | 
						|
										CreatedAt:   metadata.CreatedAt.Unix(),
							 | 
						|
										CustomData:  make(map[string]string),
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Set end time and duration if available
							 | 
						|
									if metadata.EndTime != nil {
							 | 
						|
										pbMetadata.EndTime = metadata.EndTime.Unix()
							 | 
						|
									}
							 | 
						|
									if metadata.Duration != nil {
							 | 
						|
										pbMetadata.DurationMs = metadata.Duration.Milliseconds()
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Convert custom data
							 | 
						|
									for key, value := range metadata.CustomData {
							 | 
						|
										if strValue, ok := value.(string); ok {
							 | 
						|
											pbMetadata.CustomData[key] = strValue
							 | 
						|
										} else {
							 | 
						|
											pbMetadata.CustomData[key] = fmt.Sprintf("%v", value)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return pbMetadata, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// readTaskLogEntries reads and filters log entries based on the request
							 | 
						|
								func (h *TaskLogHandler) readTaskLogEntries(logDir string, request *worker_pb.TaskLogRequest) ([]*worker_pb.TaskLogEntry, error) {
							 | 
						|
									entries, err := ReadTaskLogs(logDir)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Apply filters
							 | 
						|
									var filteredEntries []TaskLogEntry
							 | 
						|
								
							 | 
						|
									for _, entry := range entries {
							 | 
						|
										// Filter by log level
							 | 
						|
										if request.LogLevel != "" && !strings.EqualFold(entry.Level, request.LogLevel) {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Filter by time range
							 | 
						|
										if request.StartTime > 0 && entry.Timestamp.Unix() < request.StartTime {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
										if request.EndTime > 0 && entry.Timestamp.Unix() > request.EndTime {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										filteredEntries = append(filteredEntries, entry)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Limit entries if requested
							 | 
						|
									if request.MaxEntries > 0 && len(filteredEntries) > int(request.MaxEntries) {
							 | 
						|
										// Take the most recent entries
							 | 
						|
										start := len(filteredEntries) - int(request.MaxEntries)
							 | 
						|
										filteredEntries = filteredEntries[start:]
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Convert to protobuf entries
							 | 
						|
									var pbEntries []*worker_pb.TaskLogEntry
							 | 
						|
									for _, entry := range filteredEntries {
							 | 
						|
										pbEntry := &worker_pb.TaskLogEntry{
							 | 
						|
											Timestamp: entry.Timestamp.Unix(),
							 | 
						|
											Level:     entry.Level,
							 | 
						|
											Message:   entry.Message,
							 | 
						|
											Fields:    make(map[string]string),
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Set progress if available
							 | 
						|
										if entry.Progress != nil {
							 | 
						|
											pbEntry.Progress = float32(*entry.Progress)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Set status if available
							 | 
						|
										if entry.Status != nil {
							 | 
						|
											pbEntry.Status = *entry.Status
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Convert fields
							 | 
						|
										for key, value := range entry.Fields {
							 | 
						|
											if strValue, ok := value.(string); ok {
							 | 
						|
												pbEntry.Fields[key] = strValue
							 | 
						|
											} else {
							 | 
						|
												pbEntry.Fields[key] = fmt.Sprintf("%v", value)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										pbEntries = append(pbEntries, pbEntry)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return pbEntries, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// ListAvailableTaskLogs returns a list of available task log directories
							 | 
						|
								func (h *TaskLogHandler) ListAvailableTaskLogs() ([]string, error) {
							 | 
						|
									entries, err := os.ReadDir(h.baseLogDir)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("failed to read base log directory: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									var taskDirs []string
							 | 
						|
									for _, entry := range entries {
							 | 
						|
										if entry.IsDir() {
							 | 
						|
											taskDirs = append(taskDirs, entry.Name())
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return taskDirs, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CleanupOldLogs removes old task logs beyond the specified limit
							 | 
						|
								func (h *TaskLogHandler) CleanupOldLogs(maxTasks int) error {
							 | 
						|
									config := TaskLoggerConfig{
							 | 
						|
										BaseLogDir: h.baseLogDir,
							 | 
						|
										MaxTasks:   maxTasks,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create a temporary logger to trigger cleanup
							 | 
						|
									tempLogger := &FileTaskLogger{
							 | 
						|
										config: config,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									tempLogger.cleanupOldLogs()
							 | 
						|
									return nil
							 | 
						|
								}
							 |