Exemple #1
3
func (q *AMQP) Connect() error {
	q.Lock()
	defer q.Unlock()

	q.headers = amqp.Table{
		"precision":        q.Precision,
		"database":         q.Database,
		"retention_policy": q.RetentionPolicy,
	}

	var connection *amqp.Connection
	// make new tls config
	tls, err := internal.GetTLSConfig(
		q.SSLCert, q.SSLKey, q.SSLCA, q.InsecureSkipVerify)
	if err != nil {
		return err
	}

	if tls != nil {
		connection, err = amqp.DialTLS(q.URL, tls)
	} else {
		connection, err = amqp.Dial(q.URL)
	}
	if err != nil {
		return err
	}
	channel, err := connection.Channel()
	if err != nil {
		return fmt.Errorf("Failed to open a channel: %s", err)
	}

	err = channel.ExchangeDeclare(
		q.Exchange, // name
		"topic",    // type
		true,       // durable
		false,      // delete when unused
		false,      // internal
		false,      // no-wait
		nil,        // arguments
	)
	if err != nil {
		return fmt.Errorf("Failed to declare an exchange: %s", err)
	}
	q.channel = channel
	go func() {
		log.Printf("Closing: %s", <-connection.NotifyClose(make(chan *amqp.Error)))
		log.Printf("Trying to reconnect")
		for err := q.Connect(); err != nil; err = q.Connect() {
			log.Println(err)
			time.Sleep(10 * time.Second)
		}

	}()
	return nil
}
Exemple #2
0
//NewServerCodec returns a new rpc.ClientCodec using AMQP on conn. serverRouting is the routing
//key with with RPC calls are received, encodingCodec is an EncodingCoding implementation. This package provdes JSONCodec and GobCodec for the JSON and Gob encodings respectively.
func NewServerCodec(conn *amqp.Connection, serverRouting string, encodingCodec EncodingCodec) (rpc.ServerCodec, error) {
	channel, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	queue, err := channel.QueueDeclare(serverRouting, false, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	messages, err := channel.Consume(queue.Name, "", true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	server := &serverCodec{
		codec: &codec{
			conn:    conn,
			channel: channel,
			routing: queue.Name,

			codec:   encodingCodec,
			message: messages,
		},
		lock:  new(sync.RWMutex),
		calls: make(map[uint64]route),
		seq:   0,
	}

	return server, err
}
Exemple #3
0
func (server *Server) ServeConn(conn *amqp.Connection, queue string) {
	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	q, err := ch.QueueDeclare(
		queue, // name
		false, // durable
		false, // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")
	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // autoAck
		false,  // exclusive
		false,  // noLocal
		false,  // noWait
		nil,    // arguments
	)
	failOnError(err, "Failed to register a consumer")
	codec := &serverCodec{
		conn:    conn,
		ch:      ch,
		msgs:    msgs,
		pending: make(map[uint64]prop),
	}
	server.s.ServeCodec(codec)
}
Exemple #4
0
//NewClientCodec returns a new rpc.ClientCodec using AMQP on conn. serverRouting is the routing
//key with with RPC calls are sent, it should be the same routing key used with NewServerCodec.
//encodingCodec is an EncodingCoding implementation. This package provdes JSONCodec and GobCodec
//for the JSON and Gob encodings respectively.
func NewClientCodec(conn *amqp.Connection, serverRouting string, encodingCodec EncodingCodec) (rpc.ClientCodec, error) {
	channel, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	queue, err := channel.QueueDeclare("", false, true, false, false, nil)
	if err != nil {
		return nil, err
	}

	serverQueue, err := channel.QueueDeclare(serverRouting, false, false, false, false, nil)
	if err != nil {
		return nil, err
	}
	if serverQueue.Consumers == 0 {
		return nil, ErrNoConsumers
	}

	message, err := channel.Consume(queue.Name, "", true, false, false, false, nil)
	client := &clientCodec{
		codec: &codec{
			conn:    conn,
			channel: channel,
			routing: queue.Name,
			codec:   encodingCodec,
			message: message,
		},

		serverRouting: serverRouting,
	}

	return client, err
}
func monitoring(uri string, queueName string, mgmtUri string, prefix string) {
	var (
		queueConn *amqp.Connection
		queueChan *amqp.Channel
		err       error
	)
	queueConn, queueChan, err = rabbitmqConnect(uri, queueName)
	if err != nil {
		return
	}
	for {
		log.Printf("fetch rabbitmq stats")
		var metrics []graphite.Metric
		for _, metric := range fetchQueueMetrics(mgmtUri, prefix) {
			metrics = append(metrics, metric)
		}
		for _, metric := range fetchExchangeMetrics(mgmtUri, prefix) {
			metrics = append(metrics, metric)
		}
		for _, metric := range metrics {
			body := []byte(metric.Name + "\t" + metric.Value + "\t" + strconv.FormatInt(metric.Timestamp, 10))
			msg := amqp.Publishing{ContentType: "text/plain", Body: body}
			err = queueChan.Publish("", queueName, false, false, msg)
			if err != nil {
				log.Printf("publish err: %s", err)
				return
			}
			//log.Printf("metric\t%s\t\t%s", metric.Name, metric.Value)
		}
		time.Sleep(time.Second * 5)
	}
	queueChan.Close()
	queueConn.Close()
}
Exemple #6
0
func CreateHelloTopology(connection *amqp.Connection) (*amqp.Channel, error) {
	channel, err := connection.Channel()
	if err != nil {
		log.Println("Failed to get channel!: ", err)
		return nil, err
	}

	err = channel.ExchangeDeclare("hello-exchange", "direct", true, false, false, false, nil)
	if err != nil {
		log.Println("Failed to declare exchange!: ", err)
		return nil, err
	}

	_, err = channel.QueueDeclare("hello-queue", false, false, false, false, nil)
	if err != nil {
		log.Println("Failed to declare queue!: ", err)
		return nil, err
	}

	err = channel.QueueBind("hello-queue", "hola", "hello-exchange", false, nil)
	if err != nil {
		log.Println("Failed to bind to queue!: ", err)
		return nil, err
	}

	return channel, nil
}
func newConsumeChannel(conn *amqp.Connection, queueName string) (<-chan amqp.Delivery, error) {
	ch, err := conn.Channel()
	if err != nil {
		return nil, err
	}
	return ch.Consume(queueName, "", false, false, false, false, nil)
}
Exemple #8
0
/*
If ch is nil, a new Connection and Channel will be created, and this publisher
will 'own' the connection. A call to close() will close both channel and connection.

If ch is provided, then this publisher will reuse the channel and
calls to close() will do nothing.
*/
func newPublisher(serverURI string, cfg *ChannelConfig, ch *amqp.Channel) *Publisher {

	var conn *amqp.Connection
	var err error

	if ch == nil {
		conn, err = amqp.Dial(serverURI)
		if err != nil {
			panic(fmt.Errorf("Failed to connect to RabbitMQ: %v", err))
		}

		ch, err = conn.Channel()
		if err != nil {
			panic(fmt.Errorf("Failed to open a channel: %v", err))
		}
	}

	_, err = ch.QueueDeclare(*cfg.Name, *cfg.Durable, *cfg.AutoDelete, *cfg.Exclusive, false, *cfg.Args)
	if err != nil {
		panic(fmt.Errorf("Failed to declare queue %s: %v", cfg.Name, err))
	}

	ch.QueuePurge(*cfg.Name, true)
	return &Publisher{exch: "", routingKey: *cfg.Name, conn: conn, ch: ch, typeTag: *cfg.TypeTag}
}
Exemple #9
0
// openChannel opens up a RabbitMQ channel.
func openChannel(conn *amqp.Connection) *amqp.Channel {
	ch, err := conn.Channel()
	if err != nil {
		log.Fatalf("Failed to open a channel: %s", err)
	}
	return ch
}
Exemple #10
0
func Consume(connection *amqp.Connection, queue string, outChannel chan *protobuf.Notification) {
	// create a channel on this connection
	channel, err := connection.Channel()
	if err != nil {
		panic(err)
	}
	defer channel.Close()

	// start consuming data
	consumerTag := queue + "-consumer"
	deliveries, err := channel.Consume(
		queue,       // name
		consumerTag, // consumerTag
		false,       // noAck
		false,       // exclusive
		false,       // noLocal
		false,       // noWait
		nil,         // arguments
	)
	if err != nil {
		panic(err)
	}
	defer channel.Cancel(consumerTag, false)

	for delivery := range deliveries {
		notif := &protobuf.Notification{}
		proto.Unmarshal(delivery.Body, notif)

		outChannel <- notif

		if err := delivery.Ack(false); err != nil {
			panic(err)
		}
	}
}
Exemple #11
0
func connect(conn *amqp.Connection, exchange string, wg *sync.WaitGroup) {
	ch, err := conn.Channel()
	utils.FailOnError(err, "Failed to open a channel")
	defer conn.Close()

	err = ch.ExchangeDeclare(
		exchange, // name
		"direct", // type
		true,     // durable
		false,    // auto-deleted
		false,    // internal
		false,    // no-wait
		nil,      // arguments
	)
	utils.FailOnError(err, "Failed to declare an exchange")

	keys := []string{"create", "update"}
	for {
		for _, key := range keys {
			go connectKey(ch, key, exchange)
		}
		log.Printf("Break \n \n")
		time.Sleep(40 * time.Second)
	}
}
Exemple #12
0
func Log(connection *amqp.Connection, inChannel chan *protobuf.Log) {
	// create a channel on this connection
	channel, err := connection.Channel()
	if err != nil {
		panic(err)
	}
	defer channel.Close()

	for log := range inChannel {
		body, err := proto.Marshal(log)
		if err != nil {
			panic(err)
		}

		err = channel.Publish(
			exchange,   // publish to an exchange
			routingKey, // routing to 0 or more queues
			false,      // mandatory
			false,      // immediate
			amqp.Publishing{
				Headers:         amqp.Table{},
				ContentType:     "",
				ContentEncoding: "",
				Body:            body,
				DeliveryMode:    amqp.Transient, // 1=non-persistent, 2=persistent
				Priority:        0,              // 0-9
				// a bunch of application/implementation-specific fields
			},
		)
		if err != nil {
			panic(err)
		}
	}
}
func receive(conn *amqp.Connection, exchange string, key string, wg *sync.WaitGroup) {
	ch, err := conn.Channel()
	utils.FailOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		exchange, // name
		"direct", // type
		true,     // durable
		false,    // auto-deleted
		false,    // internal
		false,    // no-wait
		nil,      // arguments
	)
	utils.FailOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	utils.FailOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,   // queue name
		key,      // routing key
		exchange, // exchange
		false,
		nil)
	utils.FailOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	utils.FailOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for msg := range msgs {
			log.Printf(" [i] %s", key)
			go processMessage(msg.Body)
		}
	}()

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever
}
Exemple #14
0
// shutdown is a general closer function for handling close gracefully
// Mostly here for both consumers and producers
// After a reconnection scenerio we are gonna call shutdown before connection
func shutdown(conn *amqp.Connection) error {
	if err := conn.Close(); err != nil {
		if amqpError, isAmqpError := err.(*amqp.Error); isAmqpError && amqpError.Code != 504 {
			return fmt.Errorf("AMQP connection close error: %s", err)
		}
	}

	return nil
}
Exemple #15
0
/**
 * Retrieves a channel from the given connection, ConnectionDefinition
 */
func OpenChannel(connection *amqp.Connection, cd ConnectionDefinition) error {
	channel, err := connection.Channel()
	if err != nil {
		return err
	}

	Channel[cd.Vhost] = channel
	return nil
}
Exemple #16
0
func (i *CLI) amqpErrorWatcher(amqpConn *amqp.Connection) {
	errChan := make(chan *amqp.Error)
	errChan = amqpConn.NotifyClose(errChan)

	err, ok := <-errChan
	if ok {
		i.logger.WithField("err", err).Error("amqp connection errored, terminating")
		i.cancel()
	}
}
Exemple #17
0
// CreateChannel is a wrapper to simplify creating a channel with its attendant
// exchange.
func CreateChannel(conn *amqp.Connection, exchange, exchangeType string) (*amqp.Channel, error) {
	ch, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	if err = ch.ExchangeDeclare(exchange, exchangeType, true, false, false, false, nil); err != nil {
		return nil, err
	}
	return ch, nil
}
Exemple #18
0
// Closes the connection
func (amqpBackend *AMQPBackend) close(channel *amqp.Channel, conn *amqp.Connection) error {
	if err := channel.Close(); err != nil {
		return fmt.Errorf("Channel Close: %s", err)
	}

	if err := conn.Close(); err != nil {
		return fmt.Errorf("Connection Close: %s", err)
	}

	return nil
}
Exemple #19
0
func Publish(messages chan *amqp.Publishing, connectionUri, exchange,
	routingKey string, mandatory, immediate bool, results chan *PublishResult) {

	var err error
	var conn *amqp.Connection
	var channel *amqp.Channel

	defer close(results)

	if conn, err = amqp.Dial(connectionUri); err != nil {
		results <- &PublishResult{"Failed to connect", err, true}
		return
	}

	defer conn.Close()

	if channel, err = conn.Channel(); err != nil {
		results <- &PublishResult{"Failed to get channel", err, true}
		return
	}

	pubAcks, pubNacks := channel.NotifyConfirm(make(chan uint64), make(chan uint64))
	chanClose := channel.NotifyClose(make(chan *amqp.Error))
	if err = channel.Confirm(false); err != nil {
		results <- &PublishResult{
			"Failed to put channel into confirm mode",
			err,
			true,
		}
		return
	}

	for message := range messages {
		err = channel.Publish(exchange, routingKey, mandatory, immediate, *message)
		if err != nil {
			results <- &PublishResult{"Failed to publish message", err, false}
			continue
		}

		select {
		case err = <-chanClose:
			results <- &PublishResult{"Channel closed!", err, true}
		case <-pubAcks:
			results <- &PublishResult{
				fmt.Sprintf("Published to exchange '%s' routing key '%v': %+v", exchange, routingKey, message),
				nil,
				false,
			}
		case <-pubNacks:
			results <- &PublishResult{"Received basic.nack for message", errors.New("'basic.nack'"), false}
		}
	}
}
Exemple #20
0
// Fire is called when an event should be sent to the message broker.k
func (q amqpConn) Fire(entry *logrus.Entry) error {
	ch, err := q.Connection.Channel()
	if err != nil {
		// Any other error other than connection closed, return.
		if err != amqp.ErrClosed {
			return err
		}
		// Attempt to connect again.
		var conn *amqp.Connection
		conn, err = amqp.Dial(q.params.URL)
		if err != nil {
			return err
		}
		ch, err = conn.Channel()
		if err != nil {
			return err
		}
	}
	defer ch.Close()

	err = ch.ExchangeDeclare(
		q.params.Exchange,
		q.params.ExchangeType,
		q.params.Durable,
		q.params.AutoDeleted,
		q.params.Internal,
		q.params.NoWait,
		nil,
	)
	if err != nil {
		return err
	}

	body, err := entry.String()
	if err != nil {
		return err
	}

	err = ch.Publish(
		q.params.Exchange,
		q.params.RoutingKey,
		q.params.Mandatory,
		q.params.Immediate,
		amqp.Publishing{
			ContentType: "application/json",
			Body:        []byte(body),
		})
	if err != nil {
		return err
	}

	return nil
}
Exemple #21
0
// Connects to the message queue, opens a channel, declares a queue
func open(cnf *config.Config) (*amqp.Connection, *amqp.Channel, amqp.Queue, error) {
	var conn *amqp.Connection
	var channel *amqp.Channel
	var queue amqp.Queue
	var err error

	conn, err = amqp.Dial(cnf.Broker)
	if err != nil {
		return conn, channel, queue, fmt.Errorf("Dial: %s", err)
	}

	channel, err = conn.Channel()
	if err != nil {
		return conn, channel, queue, fmt.Errorf("Channel: %s", err)
	}

	if err := channel.ExchangeDeclare(
		cnf.Exchange,     // name of the exchange
		cnf.ExchangeType, // type
		true,             // durable
		false,            // delete when complete
		false,            // internal
		false,            // noWait
		nil,              // arguments
	); err != nil {
		return conn, channel, queue, fmt.Errorf("Exchange: %s", err)
	}

	queue, err = channel.QueueDeclare(
		cnf.DefaultQueue, // name
		true,             // durable
		false,            // delete when unused
		false,            // exclusive
		false,            // no-wait
		nil,              // arguments
	)
	if err != nil {
		return conn, channel, queue, fmt.Errorf("Queue Declare: %s", err)
	}

	if err := channel.QueueBind(
		queue.Name,     // name of the queue
		cnf.BindingKey, // binding key
		cnf.Exchange,   // source exchange
		false,          // noWait
		nil,            // arguments
	); err != nil {
		return conn, channel, queue, fmt.Errorf("Queue Bind: %s", err)
	}

	return conn, channel, queue, nil
}
Exemple #22
0
// Closes the connection
func (amqpBroker *AMQPBroker) close(channel *amqp.Channel, conn *amqp.Connection) error {
	if channel != nil {
		if err := channel.Close(); err != nil {
			return fmt.Errorf("Channel Close: %s", err)
		}
	}

	if conn != nil {
		if err := conn.Close(); err != nil {
			return fmt.Errorf("Connection Close: %s", err)
		}
	}

	return nil
}
Exemple #23
0
func (c *DefaultClient) connect(reconnect bool) (chan *AMQP.Error, error) {
	var err error
	var conn *AMQP.Connection
	for retries := 0; reconnect || retries < ConnectRetries; retries++ {
		conn, err = AMQP.Dial(c.url)
		if err == nil {
			break
		}
		c.ctx.Warnf("Could not connect to AMQP server (%s). Retry attempt %d, reconnect is %v...", err.Error(), retries+1, reconnect)
		<-time.After(ConnectRetryDelay)
	}
	if err != nil {
		return nil, fmt.Errorf("Could not connect to AMQP server (%s)", err)
	}

	closed := make(chan *AMQP.Error)
	conn.NotifyClose(closed)
	go func() {
		err := <-closed
		if err != nil {
			c.ctx.Warnf("Connection closed (%s). Reconnecting...", err)
			c.connect(true)
		} else {
			c.ctx.Info("Connection closed")
		}
	}()

	c.conn = conn
	c.ctx.Info("Connected to AMQP")

	if reconnect {
		c.mutex.Lock()
		defer c.mutex.Unlock()
		for user, channel := range c.channels {
			channel.Close()
			channel, err = c.conn.Channel()
			if err != nil {
				c.ctx.Warnf("Failed to reopen channel %s for %s (%s)", user.name, user.exchange, err)
				continue
			}
			c.ctx.Infof("Reopened channel %s for %s", user.name, user.exchange)
			user.channel = channel
			c.channels[user] = channel
		}
	}

	return closed, nil
}
Exemple #24
0
// Build a Consumer to use in Workers that will consume an AMQP queue
//
// @param {String} tag(consumer_tag)
// @param {*amqp.Connection} connection
// @return {*Consumer}, {error}
// @api public
func BuildConsumer(tag string, connection *amqp.Connection) (*Consumer, error) {
	c := &Consumer{
		Conn:    connection,
		Channel: nil,
		Tag:     tag,
		Done:    make(chan error),
	}

	var err error

	c.Channel, err = connection.Channel()
	if err != nil {
		return nil, fmt.Errorf("Channel: %s", err)
	}

	return c, nil
}
Exemple #25
0
func channelHelper(
	tc *testClient,
	conn *amqpclient.Connection,
) (
	*amqpclient.Channel,
	chan amqpclient.Return,
	chan *amqpclient.Error,
) {
	ch, err := conn.Channel()
	if err != nil {
		panic("Bad channel!")
	}
	retChan := make(chan amqpclient.Return)
	closeChan := make(chan *amqpclient.Error)
	ch.NotifyReturn(retChan)
	ch.NotifyClose(closeChan)
	return ch, retChan, closeChan
}
Exemple #26
0
// redial continually connects to the URL, exiting the program when no longer possible
func redial(ctx context.Context, url, exchange string) chan chan session {
	sessions := make(chan chan session)

	go func() {
		sess := make(chan session)
		defer close(sessions)

		for {
			select {
			case sessions <- sess:
			case <-ctx.Done():
				log.Info("shutting down session factory")
				return
			}

			connected := false
			var conn *amqp.Connection
			var ch *amqp.Channel
			var err error
			for !connected {
				log.Debug("dialing amqp url: %s", url)
				conn, err = amqp.Dial(url)
				if err != nil {
					log.Error(3, "cannot (re)dial: %v: %q", err, url)
					time.Sleep(time.Second)
					continue
				}
				log.Debug("connected to %s", url)

				log.Debug("creating new channel on AMQP connection.")
				ch, err = conn.Channel()
				if err != nil {
					log.Error(3, "cannot create channel: %v", err)
					conn.Close()
					time.Sleep(time.Second)
					continue
				}
				log.Debug("Ensuring that %s topic exchange exists on AMQP server.", exchange)
				if err := ch.ExchangeDeclare(exchange, "topic", true, false, false, false, nil); err != nil {
					log.Error(3, "cannot declare topic exchange: %v", err)
					conn.Close()
					time.Sleep(time.Second)
				}
				log.Debug("Successfully connected to RabbitMQ.")
				connected = true
			}

			select {
			case sess <- session{conn, ch}:
			case <-ctx.Done():
				log.Info("shutting down new session")
				return
			}
		}
	}()

	return sessions
}
Exemple #27
0
// Start endpoint, stopping first if it was already running. update index
func (m *EndpointMgr) RestartEndpoint(conn *amqp.Connection, cfg config.EndpointConfig) error {
	if old, ok := m.eps[cfg.Name]; ok {
		old.Stop()
		delete(m.eps, cfg.Name)
	}
	ch, err := conn.Channel()
	if err != nil {
		return err
	}
	s, err := GetStrategy(cfg.ConsumerStrategy, cfg.DeliveryStrategy)
	if err != nil {
		return err
	}
	ep, err := New(ch, cfg, s)
	if err != nil {
		return err
	}
	m.eps[cfg.Name] = ep
	return nil
}
Exemple #28
0
func (ab *AmqpBeat) handleDisconnect(conn *amqp.Connection) {
	connClosed := make(chan *amqp.Error)
	conn.NotifyClose(connClosed)

	go func() {
		// This select is here to remove a race condition that
		// can appear since stopping the beat triggers
		// a disconnect. Calling ab.Stop() in that
		// case causes a panic, since the stop chan is
		// already closed
		select {
		case <-ab.stop:
		// already stooped, fall through
		case <-connClosed:
			// server disconnect received
			logp.Info("Detected AMQP connection closed. Stopping.")
			ab.Stop()
		}
	}()
}
Exemple #29
0
// Function to open channel on the amqp connection
func OpenChannel(conn *amqp.Connection, msgType string) (*amqp.Channel, error) {
	// Open a channel to communicate with the server
	channel, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	// Declare the exchange to use when publishing
	if err := channel.ExchangeDeclare(
		msgType,
		"direct",
		false,
		false,
		false,
		false,
		nil,
	); err != nil {
		return nil, err
	}

	// Declare the queue to use when publishing
	channel.QueueDeclare(
		msgType,
		false,
		true,
		false,
		false,
		nil,
	)

	// Bind the queue to the exchange
	channel.QueueBind(
		msgType,
		"",
		msgType,
		false,
		nil,
	)

	return channel, nil
}
func newAMQPLogWriter(ctx gocontext.Context, conn *amqp.Connection, jobID uint64) (*amqpLogWriter, error) {
	channel, err := conn.Channel()
	if err != nil {
		return nil, err
	}

	err = channel.ExchangeDeclare("reporting", "topic", true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	_, err = channel.QueueDeclare("reporting.jobs.logs", true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	err = channel.QueueBind("reporting.jobs.logs", "reporting.jobs.logs", "reporting", false, nil)
	if err != nil {
		return nil, err
	}

	writer := &amqpLogWriter{
		ctx:       context.FromComponent(ctx, "log_writer"),
		amqpConn:  conn,
		amqpChan:  channel,
		jobID:     jobID,
		closeChan: make(chan struct{}),
		buffer:    new(bytes.Buffer),
		timer:     time.NewTimer(time.Hour),
		timeout:   0,
	}

	context.LoggerFromContext(ctx).WithFields(logrus.Fields{
		"writer": writer,
		"job_id": jobID,
	}).Debug("created new log writer")

	go writer.flushRegularly()

	return writer, nil
}