code: func convertSelector(ctx context.Context, cc swarmapi.ControlClient, selector *backend.LogSelector) (*swarmapi.LogSelector, error) {
  	// don't rely on swarmkit to resolve IDs, do it ourselves
  	swarmSelector := &swarmapi.LogSelector{}
  	for _, s := range selector.Services {
  		service, err := getService(ctx, cc, s, false)
  		if err != nil {
  			return nil, err
  		}
  		c := service.Spec.Task.GetContainer()
  		if c == nil {
  			return nil, errors.New("logs only supported on container tasks")
  		}
  		swarmSelector.ServiceIDs = append(swarmSelector.ServiceIDs, service.ID)
  	}
  	for _, t := range selector.Tasks {
  		task, err := getTask(ctx, cc, t)
  		if err != nil {
  			return nil, err
  		}
  		c := task.Spec.GetContainer()
  		if c == nil {
  			return nil, errors.New("logs only supported on container tasks")
  		}
  		swarmSelector.TaskIDs = append(swarmSelector.TaskIDs, task.ID)
  	}
  	return swarmSelector, nil
  }
docs: // convertSelector takes a backend.LogSelector, which contains raw names that
  // may or may not be valid, and converts them to an api.LogSelector proto. It
  // returns an error if something fails