github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/message/group.go (about)

     1  package message
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/mongodb/grip/level"
     9  )
    10  
    11  // GroupComposer handles groups of composers as a single message,
    12  // joining messages with a new line for the string format and returning a
    13  // slice of interfaces for the Raw() form.
    14  //
    15  // Unlike most composer types, the GroupComposer is exported, and
    16  // provides the additional Messages() method to access the composer
    17  // objects as a slice.
    18  type GroupComposer struct {
    19  	messages []Composer
    20  	mutex    sync.RWMutex
    21  }
    22  
    23  // NewGroupComposer returns a GroupComposer object from a slice of
    24  // Composers.
    25  func NewGroupComposer(msgs []Composer) Composer {
    26  	return &GroupComposer{
    27  		messages: msgs,
    28  	}
    29  }
    30  
    31  // NewGroupComposerWithPriority constructs a group composer from a collection of composers.
    32  func NewGroupComposerWithPriority(p level.Priority, msgs []Composer) Composer {
    33  	cmp := NewGroupComposer(msgs)
    34  	_ = cmp.SetPriority(p)
    35  	return cmp
    36  }
    37  
    38  // MakeGroupComposer provides a variadic interface for creating a
    39  // GroupComposer.
    40  func MakeGroupComposer(msgs ...Composer) Composer {
    41  	return NewGroupComposer(msgs)
    42  }
    43  
    44  // String satisfies the fmt.Stringer interface, and returns a string
    45  // of the string form of all constituent composers joined with a newline.
    46  func (g *GroupComposer) String() string {
    47  	g.mutex.RLock()
    48  	defer g.mutex.RUnlock()
    49  
    50  	if len(g.messages) == 1 && g.messages[0].Loggable() {
    51  		return g.messages[0].String()
    52  
    53  	}
    54  
    55  	out := []string{}
    56  	for _, m := range g.messages {
    57  		if m == nil {
    58  			continue
    59  		}
    60  		if m.Loggable() {
    61  			out = append(out, m.String())
    62  		}
    63  	}
    64  
    65  	return strings.Join(out, "\n")
    66  }
    67  
    68  // Raw returns a slice of interfaces containing the raw form of all
    69  // the constituent composers.
    70  func (g *GroupComposer) Raw() interface{} {
    71  	g.mutex.RLock()
    72  	defer g.mutex.RUnlock()
    73  
    74  	if len(g.messages) == 1 && g.messages[0].Loggable() {
    75  		return g.messages[0].Raw()
    76  	}
    77  
    78  	out := []interface{}{}
    79  	for _, m := range g.messages {
    80  		if m == nil {
    81  			continue
    82  		}
    83  		if m.Loggable() {
    84  			out = append(out, m.Raw())
    85  		}
    86  	}
    87  
    88  	return out
    89  }
    90  
    91  // Loggable returns true if at least one of the constituent Composers
    92  // is loggable.
    93  func (g *GroupComposer) Loggable() bool {
    94  	g.mutex.RLock()
    95  	defer g.mutex.RUnlock()
    96  
    97  	for _, m := range g.messages {
    98  		if m == nil {
    99  			continue
   100  		}
   101  		if m.Loggable() {
   102  			return true
   103  		}
   104  	}
   105  
   106  	return false
   107  }
   108  
   109  // Priority returns the highest priority of the constituent Composers.
   110  func (g *GroupComposer) Priority() level.Priority {
   111  	var highest level.Priority
   112  
   113  	g.mutex.RLock()
   114  	defer g.mutex.RUnlock()
   115  
   116  	for _, m := range g.messages {
   117  		if m == nil {
   118  			continue
   119  		}
   120  		pri := m.Priority()
   121  		if pri > highest {
   122  			highest = pri
   123  		}
   124  	}
   125  
   126  	return highest
   127  }
   128  
   129  // SetPriority sets the priority of all constituent Composers *only*
   130  // if the existing level is unset, and does not propagate an error,
   131  // but will *not* unset the level of the compser and will return an error
   132  // in this case.
   133  func (g *GroupComposer) SetPriority(l level.Priority) error {
   134  	if l == level.Invalid {
   135  		return fmt.Errorf("cannot set priority to an invalid setting")
   136  	}
   137  
   138  	g.mutex.RLock()
   139  	defer g.mutex.RUnlock()
   140  
   141  	for _, m := range g.messages {
   142  		if m == nil {
   143  			continue
   144  		}
   145  
   146  		if m.Priority() == level.Invalid {
   147  			_ = m.SetPriority(l)
   148  		}
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  // Messages returns a the underlying collection of messages.
   155  func (g *GroupComposer) Messages() []Composer {
   156  	g.mutex.RLock()
   157  	defer g.mutex.RUnlock()
   158  
   159  	return g.messages
   160  }
   161  
   162  // Add supports adding messages to an existing group composer.
   163  func (g *GroupComposer) Add(msg Composer) {
   164  	g.mutex.Lock()
   165  	defer g.mutex.Unlock()
   166  
   167  	g.messages = append(g.messages, msg)
   168  }
   169  
   170  // Extend makes it possible to add a group of messages to an existing
   171  // group composer.
   172  func (g *GroupComposer) Extend(msg []Composer) {
   173  	g.mutex.Lock()
   174  	defer g.mutex.Unlock()
   175  
   176  	g.messages = append(g.messages, msg...)
   177  }
   178  
   179  // Append provides a variadic alternative to the Extend method.
   180  func (g *GroupComposer) Append(msgs ...Composer) {
   181  	g.Extend(msgs)
   182  }
   183  
   184  // Annotate calls the Annotate method of every non-nil component
   185  // Composer.
   186  func (g *GroupComposer) Annotate(k string, v interface{}) error {
   187  	g.mutex.Lock()
   188  	defer g.mutex.Unlock()
   189  
   190  	for _, m := range g.messages {
   191  		if m == nil {
   192  			continue
   193  		}
   194  
   195  		_ = m.Annotate(k, v)
   196  	}
   197  
   198  	return nil
   199  }