# Buggy code
def _stream_logs(
    byte_size,  # pylint: disable=too-many-locals, too-many-statements, too-many-branches
    timeout_in_seconds,
    blob_service,
    container_name,
    blob_name,
    raise_error_on_failure,
):
    colorama.init()
    stream = BytesIO()
    metadata = {}
    start = 0
    end = byte_size - 1
    available = 0
    sleep_time = 1
    max_sleep_time = 15
    num_fails = 0
    num_fails_for_backoff = 3
    consecutive_sleep_in_sec = 0

    # Try to get the initial properties so there's no waiting.
    # If the storage call fails, we'll just sleep and try again after.
    try:
        props = blob_service.get_blob_properties(
            container_name=container_name, blob_name=blob_name
        )
        metadata = props.metadata
        available = props.properties.content_length
    except (AttributeError, AzureHttpError):
        pass

    while _blob_is_not_complete(metadata) or start < available:
        while start < available:
            # Success! Reset our polling backoff.
            sleep_time = 1
            num_fails = 0
            consecutive_sleep_in_sec = 0

            try:
                old_byte_size = len(stream.getvalue())
                blob_service.get_blob_to_stream(
                    container_name=container_name,
                    blob_name=blob_name,
                    start_range=start,
                    end_range=end,
                    stream=stream,
                )

                curr_bytes = stream.getvalue()
                new_byte_size = len(curr_bytes)
                amount_read = new_byte_size - old_byte_size
                start += amount_read
                end = start + byte_size - 1

                # Only scan what's newly read. If nothing is read, default to 0.
                min_scan_range = max(new_byte_size - amount_read - 1, 0)
                for i in range(new_byte_size - 1, min_scan_range, -1):
                    if curr_bytes[i - 1 : i + 1] == b"\r\n":
                        flush = curr_bytes[:i]  # won't print \n
                        stream = BytesIO()
                        stream.write(curr_bytes[i + 1 :])
                        print(flush.decode("utf-8", errors="ignore"))
                        break
            except AzureHttpError as ae:
                if ae.status_code != 404:
                    raise CLIError(ae)
            except KeyboardInterrupt:
                curr_bytes = stream.getvalue()
                if curr_bytes:
                    print(curr_bytes.decode("utf-8", errors="ignore"))
                return

        try:
            props = blob_service.get_blob_properties(
                container_name=container_name, blob_name=blob_name
            )
            metadata = props.metadata
            available = props.properties.content_length
        except AzureHttpError as ae:
            if ae.status_code != 404:
                raise CLIError(ae)
        except KeyboardInterrupt:
            if curr_bytes:
                print(curr_bytes.decode("utf-8", errors="ignore"))
            return
        except Exception as err:
            raise CLIError(err)

        if consecutive_sleep_in_sec > timeout_in_seconds:
            # Flush anything remaining in the buffer - this would be the case
            # if the file has expired and we weren't able to detect any \r\n
            curr_bytes = stream.getvalue()
            if curr_bytes:
                print(curr_bytes.decode("utf-8", errors="ignore"))

            logger.warning(
                "Failed to find any new logs in %d seconds. Client will stop polling for additional logs.",
                consecutive_sleep_in_sec,
            )
            return

        # If no new data available but not complete, sleep before trying to process additional data.
        if _blob_is_not_complete(metadata) and start >= available:
            num_fails += 1

            logger.debug("Failed to find new content %d times in a row", num_fails)
            if num_fails >= num_fails_for_backoff:
                num_fails = 0
                sleep_time = min(sleep_time * 2, max_sleep_time)
                logger.debug("Resetting failure count to %d", num_fails)

            # 1.0 <= x < 2.0
            rnd = uniform(1, 2)
            total_sleep_time = sleep_time + rnd
            consecutive_sleep_in_sec += total_sleep_time
            logger.debug(
                "Base sleep time: %d, random delay: %d, total: %d, consecutive: %d",
                sleep_time,
                rnd,
                total_sleep_time,
                consecutive_sleep_in_sec,
            )
            time.sleep(total_sleep_time)

    # One final check to see if there's anything in the buffer to flush
    # E.g., metadata has been set and start == available, but the log file
    # didn't end in \r\n, so we were unable to flush out the final contents.
    curr_bytes = stream.getvalue()
    if curr_bytes:
        print(curr_bytes.decode("utf-8", errors="ignore"))

    build_status = _get_build_status(metadata).lower()
    logger.debug("Build status was: '%s'", build_status)

    if raise_error_on_failure:
        if build_status == "internalerror" or build_status == "failed":
            raise CLIError("Build failed")
        elif build_status == "timedout":
            raise CLIError("Build timed out")
        elif build_status == "canceled":
            raise CLIError("Build was canceled")


# Traceback (UnicodeEncodeError)
az acr build -r myreg --no-push false -t myreg.azurecr.io/repo/myapp:master -f ./Dockerfile .
Sending build context (43.398 KiB) to ACR.
Queued a build with build ID: aah
Waiting for a build agent...
time="2018-05-22T21:11:59Z" level=info msg="Running command docker login -u 00000000-0000-0000-0000-000000000000 --password-stdin myreg.azurecr.io"
Login Succeeded
time="2018-05-22T21:12:01Z" level=info msg="Untarring to /root/acr-builder/src"
time="2018-05-22T21:12:01Z" level=info msg="Running command docker build --pull -f ./Dockerfile -t myreg.azurecr.io/repo/myapp:master ."
Sending build context to Docker daemon  220.2kB

Step 1/8 : FROM node:carbon
carbon: Pulling from library/node
3d77ce4481b1: Already exists
534514c83d69: Already exists
d562b1c3ac3f: Already exists
4b85e68dc01d: Already exists
f6a66c5de9db: Already exists
7a4e7d9a081d: Already exists
49f75af57b99: Pulling fs layer
454abe4fb374: Pulling fs layer
454abe4fb374: Verifying Checksum
454abe4fb374: Download complete
49f75af57b99: Verifying Checksum
49f75af57b99: Download complete
49f75af57b99: Pull complete
454abe4fb374: Pull complete
Digest: sha256:19efc0ee886057870334b4b4b4a505ea8a158d135f592cc367ab8840b684f6c0
Status: Downloaded newer image for node:carbon
---> 037acc6a00bc
Step 2/8 : WORKDIR /usr/src/app
Removing intermediate container ade8e4293757
---> 5fa54edc3ae3
Step 3/8 : COPY package*.json ./
---> 295c9eeaf7fc
Step 4/8 : RUN npm install
---> Running in 6846edad6a09
npm WARN deprecatedERROR: 'ascii' codec can't encode character u'\U0001f64c' in position 29: ordinal not in range(128)
Traceback (most recent call last):
File "/usr/lib64/az/lib/python2.7/site-packages/knack/cli.py", line 197, in invoke
cmd_result = self.invocation.execute(args)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/core/commands/__init__.py", line 332, in execute
six.reraise(*sys.exc_info())
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/core/commands/__init__.py", line 306, in execute
result = cmd(params)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/core/commands/__init__.py", line 167, in __call__
return super(AzCliCommand, self).__call__(*args, **kwargs)
File "/usr/lib64/az/lib/python2.7/site-packages/knack/commands.py", line 109, in __call__
return self.handler(*args, **kwargs)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/core/__init__.py", line 420, in default_command_handler
result = op(**command_args)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/command_modules/acr/build.py", line 330, in acr_build
return acr_build_show_logs(client, build_id, registry_name, resource_group_name, True)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/command_modules/acr/build.py", line 71, in acr_build_show_logs
raise_error_on_failure=raise_error_on_failure)
File "/usr/lib64/az/lib/python2.7/site-packages/azure/cli/command_modules/acr/build.py", line 132, in _stream_logs
print(flush.decode('utf-8', errors='ignore'))
File "/usr/lib64/az/lib/python2.7/site-packages/colorama/ansitowin32.py", line 40, in write
self.__convertor.write(text)
File "/usr/lib64/az/lib/python2.7/site-packages/colorama/ansitowin32.py", line 141, in write
self.write_and_convert(text)
File "/usr/lib64/az/lib/python2.7/site-packages/colorama/ansitowin32.py", line 166, in write_and_convert
self.write_plain_text(text, cursor, start)
File "/usr/lib64/az/lib/python2.7/site-packages/colorama/ansitowin32.py", line 174, in write_plain_text
self.wrapped.write(text[start:end])
UnicodeEncodeError: 'ascii' codec can't encode character u'\U0001f64c' in position 29: ordinal not in range(128)

# Fixed code
def _stream_logs(
    no_format,  # pylint: disable=too-many-locals, too-many-statements, too-many-branches
    byte_size,
    timeout_in_seconds,
    blob_service,
    container_name,
    blob_name,
    raise_error_on_failure,
):
    if not no_format:
        colorama.init()
    stream = BytesIO()
    metadata = {}
    start = 0
    end = byte_size - 1
    available = 0
    sleep_time = 1
    max_sleep_time = 15
    num_fails = 0
    num_fails_for_backoff = 3
    consecutive_sleep_in_sec = 0

    # Try to get the initial properties so there's no waiting.
    # If the storage call fails, we'll just sleep and try again after.
    try:
        props = blob_service.get_blob_properties(
            container_name=container_name, blob_name=blob_name
        )
        metadata = props.metadata
        available = props.properties.content_length
    except (AttributeError, AzureHttpError):
        pass

    while _blob_is_not_complete(metadata) or start < available:
        while start < available:
            # Success! Reset our polling backoff.
            sleep_time = 1
            num_fails = 0
            consecutive_sleep_in_sec = 0

            try:
                old_byte_size = len(stream.getvalue())
                blob_service.get_blob_to_stream(
                    container_name=container_name,
                    blob_name=blob_name,
                    start_range=start,
                    end_range=end,
                    stream=stream,
                )

                curr_bytes = stream.getvalue()
                new_byte_size = len(curr_bytes)
                amount_read = new_byte_size - old_byte_size
                start += amount_read
                end = start + byte_size - 1

                # Only scan what's newly read. If nothing is read, default to 0.
                min_scan_range = max(new_byte_size - amount_read - 1, 0)
                for i in range(new_byte_size - 1, min_scan_range, -1):
                    if curr_bytes[i - 1 : i + 1] == b"\r\n":
                        flush = curr_bytes[:i]  # won't print \n
                        stream = BytesIO()
                        stream.write(curr_bytes[i + 1 :])
                        print(flush.decode("utf-8", errors="ignore"))
                        break
            except AzureHttpError as ae:
                if ae.status_code != 404:
                    raise CLIError(ae)
            except KeyboardInterrupt:
                curr_bytes = stream.getvalue()
                if curr_bytes:
                    print(curr_bytes.decode("utf-8", errors="ignore"))
                return

        try:
            props = blob_service.get_blob_properties(
                container_name=container_name, blob_name=blob_name
            )
            metadata = props.metadata
            available = props.properties.content_length
        except AzureHttpError as ae:
            if ae.status_code != 404:
                raise CLIError(ae)
        except KeyboardInterrupt:
            if curr_bytes:
                print(curr_bytes.decode("utf-8", errors="ignore"))
            return
        except Exception as err:
            raise CLIError(err)

        if consecutive_sleep_in_sec > timeout_in_seconds:
            # Flush anything remaining in the buffer - this would be the case
            # if the file has expired and we weren't able to detect any \r\n
            curr_bytes = stream.getvalue()
            if curr_bytes:
                print(curr_bytes.decode("utf-8", errors="ignore"))

            logger.warning(
                "Failed to find any new logs in %d seconds. Client will stop polling for additional logs.",
                consecutive_sleep_in_sec,
            )
            return

        # If no new data available but not complete, sleep before trying to process additional data.
        if _blob_is_not_complete(metadata) and start >= available:
            num_fails += 1

            logger.debug("Failed to find new content %d times in a row", num_fails)
            if num_fails >= num_fails_for_backoff:
                num_fails = 0
                sleep_time = min(sleep_time * 2, max_sleep_time)
                logger.debug("Resetting failure count to %d", num_fails)

            # 1.0 <= x < 2.0
            rnd = uniform(1, 2)
            total_sleep_time = sleep_time + rnd
            consecutive_sleep_in_sec += total_sleep_time
            logger.debug(
                "Base sleep time: %d, random delay: %d, total: %d, consecutive: %d",
                sleep_time,
                rnd,
                total_sleep_time,
                consecutive_sleep_in_sec,
            )
            time.sleep(total_sleep_time)

    # One final check to see if there's anything in the buffer to flush
    # E.g., metadata has been set and start == available, but the log file
    # didn't end in \r\n, so we were unable to flush out the final contents.
    curr_bytes = stream.getvalue()
    if curr_bytes:
        print(curr_bytes.decode("utf-8", errors="ignore"))

    build_status = _get_build_status(metadata).lower()
    logger.debug("Build status was: '%s'", build_status)

    if raise_error_on_failure:
        if build_status == "internalerror" or build_status == "failed":
            raise CLIError("Build failed")
        elif build_status == "timedout":
            raise CLIError("Build timed out")
        elif build_status == "canceled":
            raise CLIError("Build was canceled")